move tests
authorAntony Lesuisse <al@openerp.com>
Sun, 9 Feb 2014 00:37:45 +0000 (01:37 +0100)
committerAntony Lesuisse <al@openerp.com>
Sun, 9 Feb 2014 00:37:45 +0000 (01:37 +0100)
move test modules
remove xml test
add phantomjs test
readd remove uninstall test (currently disabled because of cr.commit)

bzr revid: al@openerp.com-20140209003745-ehrx8ikwkmusa644

133 files changed:
openerp/addons/__init__.py
openerp/addons/base/__openerp__.py
openerp/addons/base/test/bug_lp541545.xml [deleted file]
openerp/addons/base/test/test_context.xml [deleted file]
openerp/addons/base/tests/__init__.py
openerp/addons/base/tests/test_acl.py [new file with mode: 0644]
openerp/addons/base/tests/test_basecase.py [new file with mode: 0644]
openerp/addons/base/tests/test_db_cursor.py [new file with mode: 0644]
openerp/addons/base/tests/test_expression.py
openerp/addons/base/tests/test_fields.py [new file with mode: 0644]
openerp/addons/base/tests/test_func.py [new file with mode: 0644]
openerp/addons/base/tests/test_ir_filters.py [new file with mode: 0644]
openerp/addons/base/tests/test_ir_sequence.py [new file with mode: 0644]
openerp/addons/base/tests/test_mail.py [new file with mode: 0755]
openerp/addons/base/tests/test_mail_examples.py [new file with mode: 0644]
openerp/addons/base/tests/test_misc.py [new file with mode: 0644]
openerp/addons/base/tests/test_orm.py [new file with mode: 0644]
openerp/addons/base/tests/test_osv.py [new file with mode: 0644]
openerp/addons/base/tests/test_phantom.py [new file with mode: 0644]
openerp/addons/base/tests/test_phantom_dummy.js [new file with mode: 0644]
openerp/addons/base/tests/test_qweb.py [new file with mode: 0644]
openerp/addons/base/tests/test_translate.py [new file with mode: 0644]
openerp/addons/base/tests/test_uninstall.py [new file with mode: 0644]
openerp/addons/base/tests/test_view_validation.py [new file with mode: 0644]
openerp/addons/base/tests/test_xmlrpc.py [new file with mode: 0644]
openerp/addons/test_convert/__init__.py [new file with mode: 0644]
openerp/addons/test_convert/__openerp__.py [new file with mode: 0644]
openerp/addons/test_convert/test_file.txt [new file with mode: 0644]
openerp/addons/test_convert/tests/__init__.py [new file with mode: 0644]
openerp/addons/test_convert/tests/test_convert.py [new file with mode: 0644]
openerp/addons/test_converter/__init__.py [new file with mode: 0644]
openerp/addons/test_converter/__openerp__.py [new file with mode: 0644]
openerp/addons/test_converter/ir.model.access.csv [new file with mode: 0644]
openerp/addons/test_converter/models.py [new file with mode: 0644]
openerp/addons/test_converter/tests/__init__.py [new file with mode: 0644]
openerp/addons/test_converter/tests/test_html.py [new file with mode: 0644]
openerp/addons/test_converter/tests/test_vectors/image [new file with mode: 0644]
openerp/addons/test_converter/tests/test_vectors/pdf [new file with mode: 0644]
openerp/addons/test_converter/tests/test_vectors/pptx [new file with mode: 0644]
openerp/addons/test_exceptions/__init__.py [new file with mode: 0644]
openerp/addons/test_exceptions/__openerp__.py [new file with mode: 0644]
openerp/addons/test_exceptions/ir.model.access.csv [new file with mode: 0644]
openerp/addons/test_exceptions/models.py [new file with mode: 0644]
openerp/addons/test_exceptions/view.xml [new file with mode: 0644]
openerp/addons/test_impex/__init__.py [new file with mode: 0644]
openerp/addons/test_impex/__openerp__.py [new file with mode: 0644]
openerp/addons/test_impex/ir.model.access.csv [new file with mode: 0644]
openerp/addons/test_impex/models.py [new file with mode: 0644]
openerp/addons/test_impex/tests/__init__.py [new file with mode: 0644]
openerp/addons/test_impex/tests/contacts.json [new file with mode: 0644]
openerp/addons/test_impex/tests/contacts_big.json [new file with mode: 0644]
openerp/addons/test_impex/tests/test_export.py [new file with mode: 0644]
openerp/addons/test_impex/tests/test_import.py [new file with mode: 0644]
openerp/addons/test_impex/tests/test_load.py [new file with mode: 0644]
openerp/addons/test_limits/__init__.py [new file with mode: 0644]
openerp/addons/test_limits/__openerp__.py [new file with mode: 0644]
openerp/addons/test_limits/ir.model.access.csv [new file with mode: 0644]
openerp/addons/test_limits/models.py [new file with mode: 0644]
openerp/addons/test_uninstall/__init__.py [new file with mode: 0644]
openerp/addons/test_uninstall/__openerp__.py [new file with mode: 0644]
openerp/addons/test_uninstall/ir.model.access.csv [new file with mode: 0644]
openerp/addons/test_uninstall/models.py [new file with mode: 0644]
openerp/addons/test_workflow/__init__.py [new file with mode: 0644]
openerp/addons/test_workflow/__openerp__.py [new file with mode: 0644]
openerp/addons/test_workflow/data.xml [new file with mode: 0644]
openerp/addons/test_workflow/ir.model.access.csv [new file with mode: 0644]
openerp/addons/test_workflow/models.py [new file with mode: 0644]
openerp/addons/test_workflow/tests/__init__.py [new file with mode: 0644]
openerp/addons/test_workflow/tests/test_workflow.py [new file with mode: 0644]
openerp/sql_db.py
openerp/tests/__init__.py
openerp/tests/addons/test_convert/__init__.py [deleted file]
openerp/tests/addons/test_convert/__openerp__.py [deleted file]
openerp/tests/addons/test_convert/test_file.txt [deleted file]
openerp/tests/addons/test_convert/tests/__init__.py [deleted file]
openerp/tests/addons/test_convert/tests/test_convert.py [deleted file]
openerp/tests/addons/test_converter/__init__.py [deleted file]
openerp/tests/addons/test_converter/__openerp__.py [deleted file]
openerp/tests/addons/test_converter/ir.model.access.csv [deleted file]
openerp/tests/addons/test_converter/models.py [deleted file]
openerp/tests/addons/test_converter/tests/__init__.py [deleted file]
openerp/tests/addons/test_converter/tests/test_html.py [deleted file]
openerp/tests/addons/test_converter/tests/test_vectors/image [deleted file]
openerp/tests/addons/test_converter/tests/test_vectors/pdf [deleted file]
openerp/tests/addons/test_converter/tests/test_vectors/pptx [deleted file]
openerp/tests/addons/test_exceptions/__init__.py [deleted file]
openerp/tests/addons/test_exceptions/__openerp__.py [deleted file]
openerp/tests/addons/test_exceptions/ir.model.access.csv [deleted file]
openerp/tests/addons/test_exceptions/models.py [deleted file]
openerp/tests/addons/test_exceptions/view.xml [deleted file]
openerp/tests/addons/test_impex/__init__.py [deleted file]
openerp/tests/addons/test_impex/__openerp__.py [deleted file]
openerp/tests/addons/test_impex/ir.model.access.csv [deleted file]
openerp/tests/addons/test_impex/models.py [deleted file]
openerp/tests/addons/test_impex/tests/__init__.py [deleted file]
openerp/tests/addons/test_impex/tests/contacts.json [deleted file]
openerp/tests/addons/test_impex/tests/contacts_big.json [deleted file]
openerp/tests/addons/test_impex/tests/test_export.py [deleted file]
openerp/tests/addons/test_impex/tests/test_import.py [deleted file]
openerp/tests/addons/test_impex/tests/test_load.py [deleted file]
openerp/tests/addons/test_limits/__init__.py [deleted file]
openerp/tests/addons/test_limits/__openerp__.py [deleted file]
openerp/tests/addons/test_limits/ir.model.access.csv [deleted file]
openerp/tests/addons/test_limits/models.py [deleted file]
openerp/tests/addons/test_uninstall/__init__.py [deleted file]
openerp/tests/addons/test_uninstall/__openerp__.py [deleted file]
openerp/tests/addons/test_uninstall/ir.model.access.csv [deleted file]
openerp/tests/addons/test_uninstall/models.py [deleted file]
openerp/tests/addons/test_workflow/__init__.py [deleted file]
openerp/tests/addons/test_workflow/__openerp__.py [deleted file]
openerp/tests/addons/test_workflow/data.xml [deleted file]
openerp/tests/addons/test_workflow/ir.model.access.csv [deleted file]
openerp/tests/addons/test_workflow/models.py [deleted file]
openerp/tests/addons/test_workflow/tests/__init__.py [deleted file]
openerp/tests/addons/test_workflow/tests/test_workflow.py [deleted file]
openerp/tests/common.py
openerp/tests/test_acl.py [deleted file]
openerp/tests/test_basecase.py [deleted file]
openerp/tests/test_db_cursor.py [deleted file]
openerp/tests/test_fields.py [deleted file]
openerp/tests/test_func.py [deleted file]
openerp/tests/test_ir_filters.py [deleted file]
openerp/tests/test_ir_sequence.py [deleted file]
openerp/tests/test_mail.py [deleted file]
openerp/tests/test_mail_examples.py [deleted file]
openerp/tests/test_misc.py [deleted file]
openerp/tests/test_orm.py [deleted file]
openerp/tests/test_osv.py [deleted file]
openerp/tests/test_qweb.py [deleted file]
openerp/tests/test_translate.py [deleted file]
openerp/tests/test_view_validation.py [deleted file]
openerp/tests/test_xmlrpc.py [deleted file]
openerp/tools/yaml_import.py

index 9554e9b..e040e19 100644 (file)
@@ -34,7 +34,4 @@ Importing them from here is deprecated.
 
 """
 
-# get_module_path is used only by base_module_quality
-from openerp.modules import get_module_resource, get_module_path
-
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 808dd18..3c3c290 100644 (file)
@@ -92,8 +92,6 @@ The kernel of OpenERP, needed for all installation.
     ],
     'test': [
         'test/base_test.yml',
-        'test/test_context.xml',
-        'test/bug_lp541545.xml',
         'test/test_osv_expression.yml',
         'test/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules.
     ],
diff --git a/openerp/addons/base/test/bug_lp541545.xml b/openerp/addons/base/test/bug_lp541545.xml
deleted file mode 100644 (file)
index 79c3c05..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-   <!-- Test count attribute for assertions -->
-   <data>
-       <assert
-               model="res.currency.rate"
-               search="[('currency_id', '=', ref('INR'))]"
-               count="1"
-               string="Rate entries for Indian rupee">
-               <test expr="True"/>
-       </assert>
-   </data>
-    
-</openerp>
diff --git a/openerp/addons/base/test/test_context.xml b/openerp/addons/base/test/test_context.xml
deleted file mode 100644 (file)
index d308fc3..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-    <!-- Test context attribute for unit tests -->
-    <data context="{'date': '2009-06-01'}">
-<!--        <assert -->
-<!--            model="res.currency"-->
-<!--            id="INR"-->
-<!--            string="Indian rupee rate in 2009">-->
-            <!-- date specified in data element's context -->
-<!--            <test expr="str(rate)">65.8287</test>-->
-<!--        </assert> -->
-        <assert
-            model="res.currency"
-            id="INR"
-            string="Indian rupee rate in 2002"
-            context="{'date': '2010-06-01'}">
-            <test expr="rate_ids and str(rate_ids[0].rate)">59.9739</test>
-        </assert>
-    </data>
-</openerp>
index 0306839..e305328 100644 (file)
@@ -1,3 +1,17 @@
+import test_acl
+import test_basecase
+import test_db_cursor
+import test_expression
+import test_fields
+import test_ir_filters
+import test_ir_sequence
+import test_mail
+import test_orm
+import test_osv
+import test_translate
+#import test_uninstall
+import test_view_validation
+import test_xmlrpc
 import test_base
 import test_expression
 import test_ir_actions
@@ -8,16 +22,4 @@ import test_res_config
 import test_res_lang
 import test_search
 import test_views
-
-checks = [
-    test_base,
-    test_expression,
-    test_ir_actions,
-    test_ir_attachment,
-    test_ir_values,
-    test_menu,
-    test_res_config,
-    test_res_lang,
-    test_search,
-    test_views,
-]
+import test_phantom
diff --git a/openerp/addons/base/tests/test_acl.py b/openerp/addons/base/tests/test_acl.py
new file mode 100644 (file)
index 0000000..f3fa0bb
--- /dev/null
@@ -0,0 +1,109 @@
+import unittest2
+from lxml import etree
+
+import openerp
+from openerp.tools.misc import mute_logger
+from openerp.tests import common
+
+# test group that demo user should not have
+GROUP_TECHNICAL_FEATURES = 'base.group_no_one'
+
+
+class TestACL(common.TransactionCase):
+
+    def setUp(self):
+        super(TestACL, self).setUp()
+        self.res_currency = self.registry('res.currency')
+        self.res_partner = self.registry('res.partner')
+        self.res_users = self.registry('res.users')
+        _, self.demo_uid = self.registry('ir.model.data').get_object_reference(self.cr, self.uid, 'base', 'user_demo')
+        self.tech_group = self.registry('ir.model.data').get_object(self.cr, self.uid,
+                                                                    *(GROUP_TECHNICAL_FEATURES.split('.')))
+
+    def test_field_visibility_restriction(self):
+        """Check that model-level ``groups`` parameter effectively restricts access to that
+           field for users who do not belong to one of the explicitly allowed groups"""
+        # Verify the test environment first
+        original_fields = self.res_currency.fields_get(self.cr, self.demo_uid, [])
+        form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form')
+        view_arch = etree.fromstring(form_view.get('arch'))
+        has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES)
+        self.assertFalse(has_tech_feat, "`demo` user should not belong to the restricted group before the test")
+        self.assertTrue('accuracy' in original_fields, "'accuracy' field must be properly visible before the test")
+        self.assertNotEquals(view_arch.xpath("//field[@name='accuracy']"), [],
+                             "Field 'accuracy' must be found in view definition before the test")
+
+        # Restrict access to the field and check it's gone
+        self.res_currency._columns['accuracy'].groups = GROUP_TECHNICAL_FEATURES
+        fields = self.res_currency.fields_get(self.cr, self.demo_uid, [])
+        form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form')
+        view_arch = etree.fromstring(form_view.get('arch'))
+        self.assertFalse('accuracy' in fields, "'accuracy' field should be gone")
+        self.assertEquals(view_arch.xpath("//field[@name='accuracy']"), [],
+                          "Field 'accuracy' must not be found in view definition")
+
+        # Make demo user a member of the restricted group and check that the field is back
+        self.tech_group.write({'users': [(4, self.demo_uid)]})
+        has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES)
+        fields = self.res_currency.fields_get(self.cr, self.demo_uid, [])
+        form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form')
+        view_arch = etree.fromstring(form_view.get('arch'))
+        #import pprint; pprint.pprint(fields); pprint.pprint(form_view)
+        self.assertTrue(has_tech_feat, "`demo` user should now belong to the restricted group")
+        self.assertTrue('accuracy' in fields, "'accuracy' field must be properly visible again")
+        self.assertNotEquals(view_arch.xpath("//field[@name='accuracy']"), [],
+                             "Field 'accuracy' must be found in view definition again")
+
+        #cleanup
+        self.tech_group.write({'users': [(3, self.demo_uid)]})
+        self.res_currency._columns['accuracy'].groups = False
+
+    @mute_logger('openerp.osv.orm')
+    def test_field_crud_restriction(self):
+        "Read/Write RPC access to restricted field should be forbidden"
+        # Verify the test environment first
+        has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES)
+        self.assertFalse(has_tech_feat, "`demo` user should not belong to the restricted group")
+        self.assert_(self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids']))
+        self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []}))
+
+        # Now restrict access to the field and check it's forbidden
+        self.res_partner._columns['bank_ids'].groups = GROUP_TECHNICAL_FEATURES
+        with self.assertRaises(openerp.osv.orm.except_orm):
+            self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids'])
+        with self.assertRaises(openerp.osv.orm.except_orm):
+            self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []})
+
+        # Add the restricted group, and check that it works again
+        self.tech_group.write({'users': [(4, self.demo_uid)]})
+        has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES)
+        self.assertTrue(has_tech_feat, "`demo` user should now belong to the restricted group")
+        self.assert_(self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids']))
+        self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []}))
+
+        #cleanup
+        self.tech_group.write({'users': [(3, self.demo_uid)]})
+        self.res_partner._columns['bank_ids'].groups = False
+
+    def test_fields_browse_restriction(self):
+        """Test access to records having restricted fields"""
+        self.res_partner._columns['email'].groups = GROUP_TECHNICAL_FEATURES
+        try:
+            P = self.res_partner
+            pid = P.search(self.cr, self.demo_uid, [], limit=1)[0]
+            part = P.browse(self.cr, self.demo_uid, pid)
+            # accessing fields must no raise exceptions...
+            part.name
+            # ... except if they are restricted
+            with self.assertRaises(openerp.osv.orm.except_orm) as cm:
+                with mute_logger('openerp.osv.orm'):
+                    part.email
+
+            self.assertEqual(cm.exception.args[0], 'Access Denied')
+        finally:
+            self.res_partner._columns['email'].groups = False
+
+if __name__ == '__main__':
+    unittest2.main()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/tests/test_basecase.py b/openerp/addons/base/tests/test_basecase.py
new file mode 100644 (file)
index 0000000..8b06eff
--- /dev/null
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+import unittest2
+
+from openerp.tests import common
+
+class test_single_transaction_case(common.SingleTransactionCase):
+    """
+    Check the whole-class transaction behavior of SingleTransactionCase.
+    """
+
+    def test_00(self):
+        """Create a partner."""
+        cr, uid = self.cr, self.uid
+        self.registry('res.partner').create(cr, uid, {'name': 'test_per_class_teardown_partner'})
+        ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
+        self.assertEqual(1, len(ids), "Test partner not found.")
+
+    def test_01(self):
+        """Find the created partner."""
+        cr, uid = self.cr, self.uid
+        ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
+        self.assertEqual(1, len(ids), "Test partner not found.")
+
+    def test_20a(self):
+        """ Create a partner with a XML ID """
+        cr, uid = self.cr, self.uid
+        res_partner = self.registry('res.partner')
+        ir_model_data = self.registry('ir.model.data')
+        pid, _ = res_partner.name_create(cr, uid, 'Mr Blue')
+        ir_model_data.create(cr, uid, {'name': 'test_partner_blue',
+                                       'module': 'base',
+                                       'model': 'res.partner',
+                                       'res_id': pid})
+    def test_20b(self):
+        """ Resolve xml id with ref() and browse_ref() """
+        cr, uid = self.cr, self.uid
+        res_partner = self.registry('res.partner')
+        xid = 'base.test_partner_blue'
+        p_ref = self.ref(xid)
+        self.assertTrue(p_ref, "ref() should resolve xid to database ID")
+        partner = res_partner.browse(cr, uid, p_ref)
+        p_browse_ref = self.browse_ref(xid)
+        self.assertEqual(partner, p_browse_ref, "browse_ref() should resolve xid to browse records")
+    
+
+
+class test_transaction_case(common.TransactionCase):
+    """
+    Check the per-method transaction behavior of TransactionCase.
+    """
+
+    def test_00(self):
+        """Create a partner."""
+        cr, uid = self.cr, self.uid
+        ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
+        self.assertEqual(0, len(ids), "Test partner found.")
+        self.registry('res.partner').create(cr, uid, {'name': 'test_per_class_teardown_partner'})
+        ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
+        self.assertEqual(1, len(ids), "Test partner not found.")
+
+    def test_01(self):
+        """Don't find the created partner."""
+        cr, uid = self.cr, self.uid
+        ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
+        self.assertEqual(0, len(ids), "Test partner found.")
+
+
+    def test_20a(self):
+        """ Create a partner with a XML ID then resolve xml id with ref() and browse_ref() """
+        cr, uid = self.cr, self.uid
+        res_partner = self.registry('res.partner')
+        ir_model_data = self.registry('ir.model.data')
+        pid, _ = res_partner.name_create(cr, uid, 'Mr Yellow')
+        ir_model_data.create(cr, uid, {'name': 'test_partner_yellow',
+                                       'module': 'base',
+                                       'model': 'res.partner',
+                                       'res_id': pid})
+        xid = 'base.test_partner_yellow'
+        p_ref = self.ref(xid)
+        self.assertEquals(p_ref, pid, "ref() should resolve xid to database ID")
+        partner = res_partner.browse(cr, uid, pid)
+        p_browse_ref = self.browse_ref(xid)
+        self.assertEqual(partner, p_browse_ref, "browse_ref() should resolve xid to browse records")
+
+if __name__ == '__main__':
+    unittest2.main()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/tests/test_db_cursor.py b/openerp/addons/base/tests/test_db_cursor.py
new file mode 100644 (file)
index 0000000..b3ce364
--- /dev/null
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+
+import unittest2
+
+import openerp
+from openerp.tools.misc import mute_logger
+from openerp.tests import common
+
+DB = common.DB
+ADMIN_USER_ID = common.ADMIN_USER_ID
+
+def registry():
+    return openerp.modules.registry.RegistryManager.get(DB)
+
+
+class test_cr_execute(unittest2.TestCase):
+    """ Try cr.execute with wrong parameters """
+
+    @mute_logger('openerp.sql_db')
+    def test_execute_bad_params(self):
+        """
+        Try to use iterable but non-list or int params in query parameters.
+        """
+        with registry().cursor(auto_commit=False) as cr:
+            with self.assertRaises(ValueError):
+                cr.execute("SELECT id FROM res_users WHERE login=%s", 'admin')
+            with self.assertRaises(ValueError):
+                cr.execute("SELECT id FROM res_users WHERE id=%s", 1)
+            with self.assertRaises(ValueError):
+                cr.execute("SELECT id FROM res_users WHERE id=%s", '1')
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 4f533a5..a05009b 100644 (file)
@@ -1,8 +1,9 @@
 import unittest2
+
+import openerp
 from openerp.osv.orm import BaseModel
 import openerp.tests.common as common
 
-
 class test_expression(common.TransactionCase):
 
     def _reinit_mock(self):
@@ -439,5 +440,13 @@ class test_expression(common.TransactionCase):
         partner_parent_id_col._auto_join = False
         state_country_id_col._auto_join = False
 
+    def test_30_normalize_domain(self):
+        expression = openerp.osv.expression
+        norm_domain = domain = ['&', (1, '=', 1), ('a', '=', 'b')]
+        assert norm_domain == expression.normalize_domain(domain), "Normalized domains should be left untouched"
+        domain = [('x', 'in', ['y', 'z']), ('a.v', '=', 'e'), '|', '|', ('a', '=', 'b'), '!', ('c', '>', 'd'), ('e', '!=', 'f'), ('g', '=', 'h')]
+        norm_domain = ['&', '&', '&'] + domain
+        assert norm_domain == expression.normalize_domain(domain), "Non-normalized domains should be properly normalized"
+
 if __name__ == '__main__':
     unittest2.main()
diff --git a/openerp/addons/base/tests/test_fields.py b/openerp/addons/base/tests/test_fields.py
new file mode 100644 (file)
index 0000000..6dd016c
--- /dev/null
@@ -0,0 +1,123 @@
+#
+# test cases for fields access, etc.
+#
+from openerp.osv import fields
+from openerp.tests import common
+
+class TestRelatedField(common.TransactionCase):
+
+    def setUp(self):
+        super(TestRelatedField, self).setUp()
+        self.partner = self.registry('res.partner')
+        self.company = self.registry('res.company')
+
+    def test_0_related(self):
+        """ test an usual related field """
+        # add a related field test_related_company_id on res.partner
+        old_columns = self.partner._columns
+        self.partner._columns = dict(old_columns)
+        self.partner._columns.update({
+            'related_company_partner_id': fields.related('company_id', 'partner_id', type='many2one', obj='res.partner'),
+        })
+
+        # find a company with a non-null partner_id
+        ids = self.company.search(self.cr, self.uid, [('partner_id', '!=', False)], limit=1)
+        id = ids[0]
+
+        # find partners that satisfy [('partner_id.company_id', '=', id)]
+        company_ids = self.company.search(self.cr, self.uid, [('partner_id', '=', id)])
+        partner_ids1 = self.partner.search(self.cr, self.uid, [('company_id', 'in', company_ids)])
+        partner_ids2 = self.partner.search(self.cr, self.uid, [('related_company_partner_id', '=', id)])
+        self.assertEqual(partner_ids1, partner_ids2)
+
+        # restore res.partner fields
+        self.partner._columns = old_columns
+
+    def do_test_company_field(self, field):
+        # get a partner with a non-null company_id
+        ids = self.partner.search(self.cr, self.uid, [('company_id', '!=', False)], limit=1)
+        partner = self.partner.browse(self.cr, self.uid, ids[0])
+
+        # check reading related field
+        self.assertEqual(partner[field], partner.company_id)
+
+        # check that search on related field is equivalent to original field
+        ids1 = self.partner.search(self.cr, self.uid, [('company_id', '=', partner.company_id.id)])
+        ids2 = self.partner.search(self.cr, self.uid, [(field, '=', partner.company_id.id)])
+        self.assertEqual(ids1, ids2)
+
+    def test_1_single_related(self):
+        """ test a related field with a single indirection like fields.related('foo') """
+        # add a related field test_related_company_id on res.partner
+        # and simulate a _inherits_reload() to populate _all_columns.
+        old_columns = self.partner._columns
+        old_all_columns = self.partner._all_columns
+        self.partner._columns = dict(old_columns)
+        self.partner._all_columns = dict(old_all_columns)
+        self.partner._columns.update({
+            'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'),
+        })
+        self.partner._all_columns.update({
+            'single_related_company_id': fields.column_info('single_related_company_id', self.partner._columns['single_related_company_id'], None, None, None)
+        })
+
+        self.do_test_company_field('single_related_company_id')
+
+        # restore res.partner fields
+        self.partner._columns = old_columns
+        self.partner._all_columns = old_all_columns
+
+    def test_2_related_related(self):
+        """ test a related field referring to a related field """
+        # add a related field on a related field on res.partner
+        # and simulate a _inherits_reload() to populate _all_columns.
+        old_columns = self.partner._columns
+        old_all_columns = self.partner._all_columns
+        self.partner._columns = dict(old_columns)
+        self.partner._all_columns = dict(old_all_columns)
+        self.partner._columns.update({
+            'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'),
+            'related_related_company_id': fields.related('single_related_company_id', type='many2one', obj='res.company'),
+        })
+        self.partner._all_columns.update({
+            'single_related_company_id': fields.column_info('single_related_company_id', self.partner._columns['single_related_company_id'], None, None, None),
+            'related_related_company_id': fields.column_info('related_related_company_id', self.partner._columns['related_related_company_id'], None, None, None)
+        })
+
+        self.do_test_company_field('related_related_company_id')
+
+        # restore res.partner fields
+        self.partner._columns = old_columns
+        self.partner._all_columns = old_all_columns
+
+    def test_3_read_write(self):
+        """ write on a related field """
+        # add a related field test_related_company_id on res.partner
+        old_columns = self.partner._columns
+        self.partner._columns = dict(old_columns)
+        self.partner._columns.update({
+            'related_company_partner_id': fields.related('company_id', 'partner_id', type='many2one', obj='res.partner'),
+        })
+
+        # find a company with a non-null partner_id
+        company_ids = self.company.search(self.cr, self.uid, [('partner_id', '!=', False)], limit=1)
+        company = self.company.browse(self.cr, self.uid, company_ids[0])
+
+        # find partners that satisfy [('partner_id.company_id', '=', company.id)]
+        partner_ids = self.partner.search(self.cr, self.uid, [('related_company_partner_id', '=', company.id)])
+        partner = self.partner.browse(self.cr, self.uid, partner_ids[0])
+
+        # create a new partner, and assign it to company
+        new_partner_id = self.partner.create(self.cr, self.uid, {'name': 'Foo'})
+        partner.write({'related_company_partner_id': new_partner_id})
+
+        company = self.company.browse(self.cr, self.uid, company_ids[0])
+        self.assertEqual(company.partner_id.id, new_partner_id)
+
+        partner = self.partner.browse(self.cr, self.uid, partner_ids[0])
+        self.assertEqual(partner.related_company_partner_id.id, new_partner_id)
+
+        # restore res.partner fields
+        self.partner._columns = old_columns
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/tests/test_func.py b/openerp/addons/base/tests/test_func.py
new file mode 100644 (file)
index 0000000..7bb2e8f
--- /dev/null
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+import functools
+import unittest2
+
+from ..tools.func import compose
+
+class TestCompose(unittest2.TestCase):
+    def test_basic(self):
+        str_add = compose(str, lambda a, b: a + b)
+        self.assertEqual(
+            str_add(1, 2),
+            "3")
+
+    def test_decorator(self):
+        """ ensure compose() can be partially applied as a decorator
+        """
+        @functools.partial(compose, unicode)
+        def mul(a, b):
+            return a * b
+
+        self.assertEqual(mul(5, 42), u"210")
+
diff --git a/openerp/addons/base/tests/test_ir_filters.py b/openerp/addons/base/tests/test_ir_filters.py
new file mode 100644 (file)
index 0000000..04ecca7
--- /dev/null
@@ -0,0 +1,278 @@
+# -*- coding: utf-8 -*-
+import functools
+
+from openerp import exceptions
+from openerp.tests import common
+
+def noid(d):
+    """ Removes `id` key from a dict so we don't have to keep these things
+    around when trying to match
+    """
+    if 'id' in d: del d['id']
+    return d
+
+class FiltersCase(common.TransactionCase):
+    def build(self, model, *args):
+        Model = self.registry(model)
+        for vars in args:
+            Model.create(self.cr, common.ADMIN_USER_ID, vars, {})
+
+class TestGetFilters(FiltersCase):
+    def setUp(self):
+        super(TestGetFilters, self).setUp()
+        self.USER = self.registry('res.users').name_search(self.cr, self.uid, 'demo')[0]
+        self.USER_ID = self.USER[0]
+
+    def test_own_filters(self):
+        self.build(
+            'ir.filters',
+            dict(name='a', user_id=self.USER_ID, model_id='ir.filters'),
+            dict(name='b', user_id=self.USER_ID, model_id='ir.filters'),
+            dict(name='c', user_id=self.USER_ID, model_id='ir.filters'),
+            dict(name='d', user_id=self.USER_ID, model_id='ir.filters'))
+
+        filters = self.registry('ir.filters').get_filters(
+            self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', is_default=False, user_id=self.USER, domain='[]', context='{}'),
+            dict(name='b', is_default=False, user_id=self.USER, domain='[]', context='{}'),
+            dict(name='c', is_default=False, user_id=self.USER, domain='[]', context='{}'),
+            dict(name='d', is_default=False, user_id=self.USER, domain='[]', context='{}'),
+        ])
+
+    def test_global_filters(self):
+        self.build(
+            'ir.filters',
+            dict(name='a', user_id=False, model_id='ir.filters'),
+            dict(name='b', user_id=False, model_id='ir.filters'),
+            dict(name='c', user_id=False, model_id='ir.filters'),
+            dict(name='d', user_id=False, model_id='ir.filters'),
+        )
+
+        filters = self.registry('ir.filters').get_filters(
+            self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', is_default=False, user_id=False, domain='[]', context='{}'),
+            dict(name='b', is_default=False, user_id=False, domain='[]', context='{}'),
+            dict(name='c', is_default=False, user_id=False, domain='[]', context='{}'),
+            dict(name='d', is_default=False, user_id=False, domain='[]', context='{}'),
+        ])
+
+    def test_no_third_party_filters(self):
+        self.build(
+            'ir.filters',
+            dict(name='a', user_id=False, model_id='ir.filters'),
+            dict(name='b', user_id=common.ADMIN_USER_ID, model_id='ir.filters'),
+            dict(name='c', user_id=self.USER_ID, model_id='ir.filters'),
+            dict(name='d', user_id=common.ADMIN_USER_ID, model_id='ir.filters')  )
+
+        filters = self.registry('ir.filters').get_filters(
+            self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', is_default=False, user_id=False, domain='[]', context='{}'),
+            dict(name='c', is_default=False, user_id=self.USER, domain='[]', context='{}'),
+        ])
+
+class TestOwnDefaults(FiltersCase):
+    def setUp(self):
+        super(TestOwnDefaults, self).setUp()
+        self.USER = self.registry('res.users').name_search(self.cr, self.uid, 'demo')[0]
+        self.USER_ID = self.USER[0]                 
+
+    def test_new_no_filter(self):
+        """
+        When creating a @is_default filter with no existing filter, that new
+        filter gets the default flag
+        """
+        Filters = self.registry('ir.filters')
+        Filters.create_or_replace(self.cr, self.USER_ID, {
+            'name': 'a',
+            'model_id': 'ir.filters',
+            'user_id': self.USER_ID,
+            'is_default': True,
+        })
+        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', user_id=self.USER, is_default=True,
+                 domain='[]', context='{}')
+        ])
+
+    def test_new_filter_not_default(self):
+        """
+        When creating a @is_default filter with existing non-default filters,
+        the new filter gets the flag
+        """
+        self.build(
+            'ir.filters',
+            dict(name='a', user_id=self.USER_ID, model_id='ir.filters'),
+            dict(name='b', user_id=self.USER_ID, model_id='ir.filters'),
+        )
+
+        Filters = self.registry('ir.filters')
+        Filters.create_or_replace(self.cr, self.USER_ID, {
+            'name': 'c',
+            'model_id': 'ir.filters',
+            'user_id': self.USER_ID,
+            'is_default': True,
+        })
+        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', user_id=self.USER, is_default=False, domain='[]', context='{}'),
+            dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}'),
+            dict(name='c', user_id=self.USER, is_default=True, domain='[]', context='{}'),
+        ])
+
+    def test_new_filter_existing_default(self):
+        """
+        When creating a @is_default filter where an existing filter is already
+        @is_default, the flag should be *moved* from the old to the new filter
+        """
+        self.build(
+            'ir.filters',
+            dict(name='a', user_id=self.USER_ID, model_id='ir.filters'),
+            dict(name='b', is_default=True, user_id=self.USER_ID, model_id='ir.filters'),
+        )
+
+        Filters = self.registry('ir.filters')
+        Filters.create_or_replace(self.cr, self.USER_ID, {
+            'name': 'c',
+            'model_id': 'ir.filters',
+            'user_id': self.USER_ID,
+            'is_default': True,
+        })
+        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', user_id=self.USER, is_default=False, domain='[]', context='{}'),
+            dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}'),
+            dict(name='c', user_id=self.USER, is_default=True, domain='[]', context='{}'),
+        ])
+
+    def test_update_filter_set_default(self):
+        """
+        When updating an existing filter to @is_default, if an other filter
+        already has the flag the flag should be moved
+        """
+        self.build(
+            'ir.filters',
+            dict(name='a', user_id=self.USER_ID, model_id='ir.filters'),
+            dict(name='b', is_default=True, user_id=self.USER_ID, model_id='ir.filters'),
+        )
+
+        Filters = self.registry('ir.filters')
+        Filters.create_or_replace(self.cr, self.USER_ID, {
+            'name': 'a',
+            'model_id': 'ir.filters',
+            'user_id': self.USER_ID,
+            'is_default': True,
+        })
+        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', user_id=self.USER, is_default=True, domain='[]', context='{}'),
+            dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}'),
+        ])
+
+class TestGlobalDefaults(FiltersCase):
+    def setUp(self):
+        super(TestGlobalDefaults, self).setUp()
+        self.USER = self.registry('res.users').name_search(self.cr, self.uid, 'demo')[0]
+        self.USER_ID = self.USER[0]
+
+    def test_new_filter_not_default(self):
+        """
+        When creating a @is_default filter with existing non-default filters,
+        the new filter gets the flag
+        """
+        self.build(
+            'ir.filters',
+            dict(name='a', user_id=False, model_id='ir.filters'),
+            dict(name='b', user_id=False, model_id='ir.filters'),
+        )
+
+        Filters = self.registry('ir.filters')
+        Filters.create_or_replace(self.cr, self.USER_ID, {
+            'name': 'c',
+            'model_id': 'ir.filters',
+            'user_id': False,
+            'is_default': True,
+        })
+        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', user_id=False, is_default=False, domain='[]', context='{}'),
+            dict(name='b', user_id=False, is_default=False, domain='[]', context='{}'),
+            dict(name='c', user_id=False, is_default=True, domain='[]', context='{}'),
+        ])
+
+    def test_new_filter_existing_default(self):
+        """
+        When creating a @is_default filter where an existing filter is already
+        @is_default, an error should be generated
+        """
+        self.build(
+            'ir.filters',
+            dict(name='a', user_id=False, model_id='ir.filters'),
+            dict(name='b', is_default=True, user_id=False, model_id='ir.filters'),
+        )
+
+        Filters = self.registry('ir.filters')
+        with self.assertRaises(exceptions.Warning):
+            Filters.create_or_replace(self.cr, self.USER_ID, {
+                'name': 'c',
+                'model_id': 'ir.filters',
+                'user_id': False,
+                'is_default': True,
+            })
+
+    def test_update_filter_set_default(self):
+        """
+        When updating an existing filter to @is_default, if an other filter
+        already has the flag an error should be generated
+        """
+        self.build(
+            'ir.filters',
+            dict(name='a', user_id=False, model_id='ir.filters'),
+            dict(name='b', is_default=True, user_id=False, model_id='ir.filters'),
+        )
+
+        Filters = self.registry('ir.filters')
+
+        with self.assertRaises(exceptions.Warning):
+            Filters.create_or_replace(self.cr, self.USER_ID, {
+                'name': 'a',
+                'model_id': 'ir.filters',
+                'user_id': False,
+                'is_default': True,
+            })
+
+    def test_update_default_filter(self):
+        """
+        Replacing the current default global filter should not generate any error
+        """
+        self.build(
+            'ir.filters',
+            dict(name='a', user_id=False, model_id='ir.filters'),
+            dict(name='b', is_default=True, user_id=False, model_id='ir.filters'),
+        )
+
+        Filters = self.registry('ir.filters')
+        context_value = "{'some_key': True}"
+        Filters.create_or_replace(self.cr, self.USER_ID, {
+            'name': 'b',
+            'model_id': 'ir.filters',
+            'user_id': False,
+            'context': context_value,
+            'is_default': True,
+        })
+        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', user_id=False, is_default=False, domain='[]', context='{}'),
+            dict(name='b', user_id=False, is_default=True, domain='[]', context=context_value),
+        ])
diff --git a/openerp/addons/base/tests/test_ir_sequence.py b/openerp/addons/base/tests/test_ir_sequence.py
new file mode 100644 (file)
index 0000000..bc428d7
--- /dev/null
@@ -0,0 +1,221 @@
+# -*- coding: utf-8 -*-
+# Run with one of these commands:
+#    > OPENERP_ADDONS_PATH='../../addons/trunk' OPENERP_PORT=8069 \
+#      OPENERP_DATABASE=yy PYTHONPATH=. python tests/test_ir_sequence.py
+#    > OPENERP_ADDONS_PATH='../../addons/trunk' OPENERP_PORT=8069 \
+#      OPENERP_DATABASE=yy nosetests tests/test_ir_sequence.py
+#    > OPENERP_ADDONS_PATH='../../../addons/trunk' OPENERP_PORT=8069 \
+#      OPENERP_DATABASE=yy PYTHONPATH=../:. unit2 test_ir_sequence
+# This assume an existing database.
+import psycopg2
+import unittest2
+
+import openerp
+from openerp.tests import common
+
+DB = common.DB
+ADMIN_USER_ID = common.ADMIN_USER_ID
+
+def registry(model):
+    return openerp.modules.registry.RegistryManager.get(DB)[model]
+
+def cursor():
+    return openerp.modules.registry.RegistryManager.get(DB).db.cursor()
+
+
+def drop_sequence(code):
+    cr = cursor()
+    for model in ['ir.sequence', 'ir.sequence.type']:
+        s = registry(model)
+        ids = s.search(cr, ADMIN_USER_ID, [('code', '=', code)])
+        s.unlink(cr, ADMIN_USER_ID, ids)
+    cr.commit()
+    cr.close()
+
+class test_ir_sequence_standard(unittest2.TestCase):
+    """ A few tests for a 'Standard' (i.e. PostgreSQL) sequence. """
+
+    def test_ir_sequence_create(self):
+        """ Try to create a sequence object. """
+        cr = cursor()
+        d = dict(code='test_sequence_type', name='Test sequence type')
+        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        d = dict(code='test_sequence_type', name='Test sequence')
+        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        cr.commit()
+        cr.close()
+
+    def test_ir_sequence_search(self):
+        """ Try a search. """
+        cr = cursor()
+        ids = registry('ir.sequence').search(cr, ADMIN_USER_ID, [], {})
+        assert ids
+        cr.commit()
+        cr.close()
+
+    def test_ir_sequence_draw(self):
+        """ Try to draw a number. """
+        cr = cursor()
+        n = registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type', {})
+        assert n
+        cr.commit()
+        cr.close()
+
+    def test_ir_sequence_draw_twice(self):
+        """ Try to draw a number from two transactions. """
+        cr0 = cursor()
+        cr1 = cursor()
+        n0 = registry('ir.sequence').next_by_code(cr0, ADMIN_USER_ID, 'test_sequence_type', {})
+        assert n0
+        n1 = registry('ir.sequence').next_by_code(cr1, ADMIN_USER_ID, 'test_sequence_type', {})
+        assert n1
+        cr0.commit()
+        cr1.commit()
+        cr0.close()
+        cr1.close()
+
+    @classmethod
+    def tearDownClass(cls):
+        drop_sequence('test_sequence_type')
+
+class test_ir_sequence_no_gap(unittest2.TestCase):
+    """ Copy of the previous tests for a 'No gap' sequence. """
+
+    def test_ir_sequence_create_no_gap(self):
+        """ Try to create a sequence object. """
+        cr = cursor()
+        d = dict(code='test_sequence_type_2', name='Test sequence type')
+        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        d = dict(code='test_sequence_type_2', name='Test sequence',
+            implementation='no_gap')
+        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        cr.commit()
+        cr.close()
+
+    def test_ir_sequence_draw_no_gap(self):
+        """ Try to draw a number. """
+        cr = cursor()
+        n = registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_2', {})
+        assert n
+        cr.commit()
+        cr.close()
+
+    def test_ir_sequence_draw_twice_no_gap(self):
+        """ Try to draw a number from two transactions.
+        This is expected to not work.
+        """
+        cr0 = cursor()
+        cr1 = cursor()
+        cr1._default_log_exceptions = False # Prevent logging a traceback
+        msg_re = '^could not obtain lock on row in relation "ir_sequence"$'
+        with self.assertRaisesRegexp(psycopg2.OperationalError, msg_re):
+            n0 = registry('ir.sequence').next_by_code(cr0, ADMIN_USER_ID, 'test_sequence_type_2', {})
+            assert n0
+            n1 = registry('ir.sequence').next_by_code(cr1, ADMIN_USER_ID, 'test_sequence_type_2', {})
+        cr0.close()
+        cr1.close()
+
+    @classmethod
+    def tearDownClass(cls):
+        drop_sequence('test_sequence_type_2')
+
+class test_ir_sequence_change_implementation(unittest2.TestCase):
+    """ Create sequence objects and change their ``implementation`` field. """
+
+    def test_ir_sequence_1_create(self):
+        """ Try to create a sequence object. """
+        cr = cursor()
+        d = dict(code='test_sequence_type_3', name='Test sequence type')
+        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        d = dict(code='test_sequence_type_3', name='Test sequence')
+        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        d = dict(code='test_sequence_type_4', name='Test sequence type')
+        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        d = dict(code='test_sequence_type_4', name='Test sequence',
+            implementation='no_gap')
+        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        cr.commit()
+        cr.close()
+
+    def test_ir_sequence_2_write(self):
+        cr = cursor()
+        ids = registry('ir.sequence').search(cr, ADMIN_USER_ID,
+            [('code', 'in', ['test_sequence_type_3', 'test_sequence_type_4'])], {})
+        registry('ir.sequence').write(cr, ADMIN_USER_ID, ids,
+            {'implementation': 'standard'}, {})
+        registry('ir.sequence').write(cr, ADMIN_USER_ID, ids,
+            {'implementation': 'no_gap'}, {})
+        cr.commit()
+        cr.close()
+
+    def test_ir_sequence_3_unlink(self):
+        cr = cursor()
+        ids = registry('ir.sequence').search(cr, ADMIN_USER_ID,
+            [('code', 'in', ['test_sequence_type_3', 'test_sequence_type_4'])], {})
+        registry('ir.sequence').unlink(cr, ADMIN_USER_ID, ids, {})
+        cr.commit()
+        cr.close()
+
+    @classmethod
+    def tearDownClass(cls):
+        drop_sequence('test_sequence_type_3')
+        drop_sequence('test_sequence_type_4')
+
+class test_ir_sequence_generate(unittest2.TestCase):
+    """ Create sequence objects and generate some values. """
+
+    def test_ir_sequence_create(self):
+        """ Try to create a sequence object. """
+        cr = cursor()
+        d = dict(code='test_sequence_type_5', name='Test sequence type')
+        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        d = dict(code='test_sequence_type_5', name='Test sequence')
+        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        cr.commit()
+        cr.close()
+
+        cr = cursor()
+        f = lambda *a: registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_5', {})
+        assert all(str(x) == f() for x in xrange(1,10))
+        cr.commit()
+        cr.close()
+
+    def test_ir_sequence_create_no_gap(self):
+        """ Try to create a sequence object. """
+        cr = cursor()
+        d = dict(code='test_sequence_type_6', name='Test sequence type')
+        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        d = dict(code='test_sequence_type_6', name='Test sequence')
+        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        assert c
+        cr.commit()
+        cr.close()
+
+        cr = cursor()
+        f = lambda *a: registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_6', {})
+        assert all(str(x) == f() for x in xrange(1,10))
+        cr.commit()
+        cr.close()
+
+    @classmethod
+    def tearDownClass(cls):
+        drop_sequence('test_sequence_type_5')
+        drop_sequence('test_sequence_type_6')
+
+
+if __name__ == '__main__':
+    unittest2.main()
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/tests/test_mail.py b/openerp/addons/base/tests/test_mail.py
new file mode 100755 (executable)
index 0000000..4ebec83
--- /dev/null
@@ -0,0 +1,381 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# This test can be run stand-alone with something like:
+# > PYTHONPATH=. python2 openerp/tests/test_misc.py
+##############################################################################
+#
+#    OpenERP, Open Source Business Applications
+#    Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import unittest2
+
+from lxml import etree
+
+from openerp.tools import html_sanitize, html_email_clean, append_content_to_html, plaintext2html, email_split
+
+import test_mail_examples
+
+
+class TestSanitizer(unittest2.TestCase):
+    """ Test the html sanitizer that filters html to remove unwanted attributes """
+
+    def test_basic_sanitizer(self):
+        cases = [
+            ("yop", "<p>yop</p>"),  # simple
+            ("lala<p>yop</p>xxx", "<p>lala</p><p>yop</p>xxx"),  # trailing text
+            ("Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci",
+                u"<p>Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci</p>"),  # unicode
+        ]
+        for content, expected in cases:
+            html = html_sanitize(content)
+            self.assertEqual(html, expected, 'html_sanitize is broken')
+
+    def test_evil_malicious_code(self):
+        # taken from https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Tests
+        cases = [
+            ("<IMG SRC=javascript:alert('XSS')>"),  # no quotes and semicolons
+            ("<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>"),  # UTF-8 Unicode encoding
+            ("<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>"),  # hex encoding
+            ("<IMG SRC=\"jav&#x0D;ascript:alert('XSS');\">"),  # embedded carriage return
+            ("<IMG SRC=\"jav&#x0A;ascript:alert('XSS');\">"),  # embedded newline
+            ("<IMG SRC=\"jav   ascript:alert('XSS');\">"),  # embedded tab
+            ("<IMG SRC=\"jav&#x09;ascript:alert('XSS');\">"),  # embedded encoded tab
+            ("<IMG SRC=\" &#14;  javascript:alert('XSS');\">"),  # spaces and meta-characters
+            ("<IMG SRC=\"javascript:alert('XSS')\""),  # half-open html
+            ("<IMG \"\"\"><SCRIPT>alert(\"XSS\")</SCRIPT>\">"),  # malformed tag
+            ("<SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>"),  # non-alpha-non-digits
+            ("<SCRIPT/SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>"),  # non-alpha-non-digits
+            ("<<SCRIPT>alert(\"XSS\");//<</SCRIPT>"),  # extraneous open brackets
+            ("<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >"),  # non-closing script tags
+            ("<INPUT TYPE=\"IMAGE\" SRC=\"javascript:alert('XSS');\">"),  # input image
+            ("<BODY BACKGROUND=\"javascript:alert('XSS')\">"),  # body image
+            ("<IMG DYNSRC=\"javascript:alert('XSS')\">"),  # img dynsrc
+            ("<IMG LOWSRC=\"javascript:alert('XSS')\">"),  # img lowsrc
+            ("<TABLE BACKGROUND=\"javascript:alert('XSS')\">"),  # table
+            ("<TABLE><TD BACKGROUND=\"javascript:alert('XSS')\">"),  # td
+            ("<DIV STYLE=\"background-image: url(javascript:alert('XSS'))\">"),  # div background
+            ("<DIV STYLE=\"background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029\">"),  # div background with unicoded exploit
+            ("<DIV STYLE=\"background-image: url(&#1;javascript:alert('XSS'))\">"),  # div background + extra characters
+            ("<IMG SRC='vbscript:msgbox(\"XSS\")'>"),  # VBscrip in an image
+            ("<BODY ONLOAD=alert('XSS')>"),  # event handler
+            ("<BR SIZE=\"&{alert('XSS')}\>"),  # & javascript includes
+            ("<LINK REL=\"stylesheet\" HREF=\"javascript:alert('XSS');\">"),  # style sheet
+            ("<LINK REL=\"stylesheet\" HREF=\"http://ha.ckers.org/xss.css\">"),  # remote style sheet
+            ("<STYLE>@import'http://ha.ckers.org/xss.css';</STYLE>"),  # remote style sheet 2
+            ("<META HTTP-EQUIV=\"Link\" Content=\"<http://ha.ckers.org/xss.css>; REL=stylesheet\">"),  # remote style sheet 3
+            ("<STYLE>BODY{-moz-binding:url(\"http://ha.ckers.org/xssmoz.xml#xss\")}</STYLE>"),  # remote style sheet 4
+            ("<IMG STYLE=\"xss:expr/*XSS*/ession(alert('XSS'))\">"),  # style attribute using a comment to break up expression
+        ]
+        for content in cases:
+            html = html_sanitize(content)
+            self.assertNotIn('javascript', html, 'html_sanitize did not remove a malicious javascript')
+            self.assertTrue('ha.ckers.org' not in html or 'http://ha.ckers.org/xss.css' in html, 'html_sanitize did not remove a malicious code in %s (%s)' % (content, html))
+
+        content = "<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->"  # down-level hidden block
+        self.assertEquals(html_sanitize(content, silent=False), '')
+
+    def test_html(self):
+        sanitized_html = html_sanitize(test_mail_examples.MISC_HTML_SOURCE)
+        for tag in ['<div', '<b', '<i', '<u', '<strike', '<li', '<blockquote', '<a href']:
+            self.assertIn(tag, sanitized_html, 'html_sanitize stripped too much of original html')
+        for attr in ['javascript']:
+            self.assertNotIn(attr, sanitized_html, 'html_sanitize did not remove enough unwanted attributes')
+
+        emails = [("Charles <charles.bidule@truc.fr>", "Charles &lt;charles.bidule@truc.fr&gt;"),
+                ("Dupuis <'tr/-: ${dupuis#$'@truc.baz.fr>", "Dupuis &lt;'tr/-: ${dupuis#$'@truc.baz.fr&gt;"),
+                ("Technical <service/technical+2@open.com>", "Technical &lt;service/technical+2@open.com&gt;"),
+                ("Div nico <div-nico@open.com>", "Div nico &lt;div-nico@open.com&gt;")]
+        for email in emails:
+            self.assertIn(email[1], html_sanitize(email[0]), 'html_sanitize stripped emails of original html')
+
+    def test_edi_source(self):
+        html = html_sanitize(test_mail_examples.EDI_LIKE_HTML_SOURCE)
+        self.assertIn('div style="font-family: \'Lucica Grande\', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF;', html,
+            'html_sanitize removed valid style attribute')
+        self.assertIn('<span style="color: #222; margin-bottom: 5px; display: block; ">', html,
+            'html_sanitize removed valid style attribute')
+        self.assertIn('img class="oe_edi_paypal_button" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"', html,
+            'html_sanitize removed valid img')
+        self.assertNotIn('</body></html>', html, 'html_sanitize did not remove extra closing tags')
+
+
+class TestCleaner(unittest2.TestCase):
+    """ Test the email cleaner function that filters the content of incoming emails """
+
+    def test_00_basic_text(self):
+        """ html_email_clean test for signatures """
+        test_data = [
+            (
+                """This is Sparta!\n--\nAdministrator\n+9988776655""",
+                ['This is Sparta!'],
+                ['Administrator', '9988776655']
+            ), (
+                """<p>--\nAdministrator</p>""",
+                [],
+                ['--', 'Administrator']
+            ), (
+                """<p>This is Sparta!\n---\nAdministrator</p>""",
+                ['This is Sparta!'],
+                ['---', 'Administrator']
+            ), (
+                """<p>--<br>Administrator</p>""",
+                [],
+                []
+            ), (
+                """<p>This is Sparta!<br/>--<br>Administrator</p>""",
+                ['This is Sparta!'],
+                []
+            ), (
+                """This is Sparta!\n>Ah bon ?\nCertes\n> Chouette !\nClair""",
+                ['This is Sparta!', 'Certes', 'Clair'],
+                ['Ah bon', 'Chouette']
+            )
+        ]
+        for test, in_lst, out_lst in test_data:
+            new_html = html_email_clean(test, remove=True)
+            for text in in_lst:
+                self.assertIn(text, new_html, 'html_email_cleaner wrongly removed content')
+            for text in out_lst:
+                self.assertNotIn(text, new_html, 'html_email_cleaner did not remove unwanted content')
+
+    def test_05_shorten(self):
+        # TEST: shorten length
+        test_str = '''<div>
+        <span>
+        </span>
+        <p>Hello, <span>Raoul</span> 
+    <bold>You</bold> are 
+    pretty</p>
+<span>Really</span>
+</div>
+'''
+        # shorten at 'H' of Hello -> should shorten after Hello,
+        html = html_email_clean(test_str, shorten=True, max_length=1, remove=True)
+        self.assertIn('Hello,', html, 'html_email_cleaner: shorten error or too short')
+        self.assertNotIn('Raoul', html, 'html_email_cleaner: shorten error or too long')
+        self.assertIn('read more', html, 'html_email_cleaner: shorten error about read more inclusion')
+        # shorten at 'are' -> should shorten after are
+        html = html_email_clean(test_str, shorten=True, max_length=17, remove=True)
+        self.assertIn('Hello,', html, 'html_email_cleaner: shorten error or too short')
+        self.assertIn('Raoul', html, 'html_email_cleaner: shorten error or too short')
+        self.assertIn('are', html, 'html_email_cleaner: shorten error or too short')
+        self.assertNotIn('pretty', html, 'html_email_cleaner: shorten error or too long')
+        self.assertNotIn('Really', html, 'html_email_cleaner: shorten error or too long')
+        self.assertIn('read more', html, 'html_email_cleaner: shorten error about read more inclusion')
+
+        # TEST: shorten in quote
+        test_str = '''<div> Blahble         
+            bluih      blouh   
+        <blockquote>This is a quote
+        <span>And this is quite a long quote, after all.</span>
+        </blockquote>
+</div>'''
+        # shorten in the quote
+        html = html_email_clean(test_str, shorten=True, max_length=25, remove=True)
+        self.assertIn('Blahble', html, 'html_email_cleaner: shorten error or too short')
+        self.assertIn('bluih', html, 'html_email_cleaner: shorten error or too short')
+        self.assertIn('blouh', html, 'html_email_cleaner: shorten error or too short')
+        self.assertNotIn('quote', html, 'html_email_cleaner: shorten error or too long')
+        self.assertIn('read more', html, 'html_email_cleaner: shorten error about read more inclusion')
+        # shorten in second word
+        html = html_email_clean(test_str, shorten=True, max_length=9, remove=True)
+        self.assertIn('Blahble', html, 'html_email_cleaner: shorten error or too short')
+        self.assertIn('bluih', html, 'html_email_cleaner: shorten error or too short')
+        self.assertNotIn('blouh', html, 'html_email_cleaner: shorten error or too short')
+        self.assertNotIn('quote', html, 'html_email_cleaner: shorten error or too long')
+        self.assertIn('read more', html, 'html_email_cleaner: shorten error about read more inclusion')
+        # shorten waaay too large
+        html = html_email_clean(test_str, shorten=True, max_length=900, remove=True)
+        self.assertIn('Blahble', html, 'html_email_cleaner: shorten error or too short')
+        self.assertIn('bluih', html, 'html_email_cleaner: shorten error or too short')
+        self.assertIn('blouh', html, 'html_email_cleaner: shorten error or too short')
+        self.assertNotIn('quote', html, 'html_email_cleaner: shorten error or too long')
+
+    def test_10_email_text(self):
+        """ html_email_clean test for text-based emails """
+        new_html = html_email_clean(test_mail_examples.TEXT_1, remove=True)
+        for ext in test_mail_examples.TEXT_1_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.TEXT_1_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
+
+        new_html = html_email_clean(test_mail_examples.TEXT_2, remove=True)
+        for ext in test_mail_examples.TEXT_2_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.TEXT_2_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
+
+    def test_20_email_html(self):
+        new_html = html_email_clean(test_mail_examples.HTML_1, remove=True)
+        for ext in test_mail_examples.HTML_1_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.HTML_1_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
+
+        new_html = html_email_clean(test_mail_examples.HTML_2, remove=True)
+        for ext in test_mail_examples.HTML_2_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.HTML_2_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
+
+        # --- MAIL ORIGINAL --- -> can't parse this one currently, too much language-dependent
+        # new_html = html_email_clean(test_mail_examples.HTML_3, remove=False)
+        # for ext in test_mail_examples.HTML_3_IN:
+        #     self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        # for ext in test_mail_examples.HTML_3_OUT:
+        #     self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
+
+    def test_30_email_msoffice(self):
+        new_html = html_email_clean(test_mail_examples.MSOFFICE_1, remove=True)
+        for ext in test_mail_examples.MSOFFICE_1_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.MSOFFICE_1_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
+
+        new_html = html_email_clean(test_mail_examples.MSOFFICE_2, remove=True)
+        for ext in test_mail_examples.MSOFFICE_2_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.MSOFFICE_2_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
+
+        new_html = html_email_clean(test_mail_examples.MSOFFICE_3, remove=True)
+        for ext in test_mail_examples.MSOFFICE_3_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.MSOFFICE_3_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
+
+    def test_40_email_hotmail(self):
+        new_html = html_email_clean(test_mail_examples.HOTMAIL_1, remove=True)
+        for ext in test_mail_examples.HOTMAIL_1_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.HOTMAIL_1_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
+
+    def test_50_email_gmail(self):
+        new_html = html_email_clean(test_mail_examples.GMAIL_1, remove=True)
+        for ext in test_mail_examples.GMAIL_1_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.GMAIL_1_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
+
+    def test_60_email_thunderbird(self):
+        new_html = html_email_clean(test_mail_examples.THUNDERBIRD_1, remove=True)
+        for ext in test_mail_examples.THUNDERBIRD_1_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.THUNDERBIRD_1_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
+
+    def test_70_read_more_and_shorten(self):
+        expand_options = {
+            'oe_expand_container_class': 'span_class',
+            'oe_expand_container_content': 'Herbert Einstein',
+            'oe_expand_separator_node': 'br_lapin',
+            'oe_expand_a_class': 'a_class',
+            'oe_expand_a_content': 'read mee',
+        }
+        new_html = html_email_clean(test_mail_examples.OERP_WEBSITE_HTML_1, remove=True, shorten=True, max_length=100, expand_options=expand_options)
+        for ext in test_mail_examples.OERP_WEBSITE_HTML_1_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.OERP_WEBSITE_HTML_1_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase overlimit content')
+        for ext in ['<span class="span_class">Herbert Einstein<br_lapin></br_lapin><a href="#" class="a_class">read mee</a></span>']:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly take into account specific expand options')
+
+        new_html = html_email_clean(test_mail_examples.OERP_WEBSITE_HTML_2, remove=True, shorten=True, max_length=200, expand_options=expand_options, protect_sections=False)
+        for ext in test_mail_examples.OERP_WEBSITE_HTML_2_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.OERP_WEBSITE_HTML_2_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase overlimit content')
+        for ext in ['<span class="span_class">Herbert Einstein<br_lapin></br_lapin><a href="#" class="a_class">read mee</a></span>']:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly take into account specific expand options')
+
+        new_html = html_email_clean(test_mail_examples.OERP_WEBSITE_HTML_2, remove=True, shorten=True, max_length=200, expand_options=expand_options, protect_sections=True)
+        for ext in test_mail_examples.OERP_WEBSITE_HTML_2_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
+        for ext in test_mail_examples.OERP_WEBSITE_HTML_2_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase overlimit content')
+        for ext in [
+                '<span class="span_class">Herbert Einstein<br_lapin></br_lapin><a href="#" class="a_class">read mee</a></span>',
+                'tasks using the gantt chart and control deadlines']:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly take into account specific expand options')
+
+    def test_70_read_more(self):
+        new_html = html_email_clean(test_mail_examples.BUG1, remove=True, shorten=True, max_length=100)
+        for ext in test_mail_examples.BUG_1_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed valid content')
+        for ext in test_mail_examples.BUG_1_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not removed invalid content')
+
+        new_html = html_email_clean(test_mail_examples.BUG2, remove=True, shorten=True, max_length=250)
+        for ext in test_mail_examples.BUG_2_IN:
+            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed valid content')
+        for ext in test_mail_examples.BUG_2_OUT:
+            self.assertNotIn(ext, new_html, 'html_email_cleaner did not removed invalid content')
+
+    def test_90_misc(self):
+        # False boolean for text must return empty string
+        new_html = html_email_clean(False)
+        self.assertEqual(new_html, False, 'html_email_cleaner did change a False in an other value.')
+
+        # Message with xml and doctype tags don't crash
+        new_html = html_email_clean(u'<?xml version="1.0" encoding="iso-8859-1"?>\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n <head>\n  <title>404 - Not Found</title>\n </head>\n <body>\n  <h1>404 - Not Found</h1>\n </body>\n</html>\n')
+        self.assertNotIn('encoding', new_html, 'html_email_cleaner did not remove correctly encoding attributes')
+
+
+class TestHtmlTools(unittest2.TestCase):
+    """ Test some of our generic utility functions about html """
+
+    def test_plaintext2html(self):
+        cases = [
+            ("First \nSecond \nThird\n \nParagraph\n\r--\nSignature paragraph", 'div',
+             "<div><p>First <br/>Second <br/>Third</p><p>Paragraph</p><p>--<br/>Signature paragraph</p></div>"),
+            ("First<p>It should be escaped</p>\nSignature", False,
+             "<p>First&lt;p&gt;It should be escaped&lt;/p&gt;<br/>Signature</p>")
+        ]
+        for content, container_tag, expected in cases:
+            html = plaintext2html(content, container_tag)
+            self.assertEqual(html, expected, 'plaintext2html is broken')
+
+    def test_append_to_html(self):
+        test_samples = [
+            ('<!DOCTYPE...><HTML encoding="blah">some <b>content</b></HtMl>', '--\nYours truly', True, True, False,
+             '<!DOCTYPE...><html encoding="blah">some <b>content</b>\n<pre>--\nYours truly</pre>\n</html>'),
+            ('<!DOCTYPE...><HTML encoding="blah">some <b>content</b></HtMl>', '--\nYours truly', True, False, False,
+             '<!DOCTYPE...><html encoding="blah">some <b>content</b>\n<p>--<br/>Yours truly</p>\n</html>'),
+            ('<html><body>some <b>content</b></body></html>', '<!DOCTYPE...>\n<html><body>\n<p>--</p>\n<p>Yours truly</p>\n</body>\n</html>', False, False, False,
+             '<html><body>some <b>content</b>\n\n\n<p>--</p>\n<p>Yours truly</p>\n\n\n</body></html>'),
+        ]
+        for html, content, plaintext_flag, preserve_flag, container_tag, expected in test_samples:
+            self.assertEqual(append_content_to_html(html, content, plaintext_flag, preserve_flag, container_tag), expected, 'append_content_to_html is broken')
+
+class TestEmailTools(unittest2.TestCase):
+    """ Test some of our generic utility functions for emails """
+
+    def test_email_split(self):
+        cases = [
+            ("John <12345@gmail.com>", ['12345@gmail.com']), # regular form 
+            ("d@x; 1@2", ['d@x', '1@2']), # semi-colon + extra space
+            ("'(ss)' <123@gmail.com>, 'foo' <foo@bar>", ['123@gmail.com','foo@bar']), # comma + single-quoting
+            ('"john@gmail.com"<johnny@gmail.com>', ['johnny@gmail.com']), # double-quoting
+            ('"<jg>" <johnny@gmail.com>', ['johnny@gmail.com']), # double-quoting with brackets 
+        ]
+        for text, expected in cases:
+            self.assertEqual(email_split(text), expected, 'email_split is broken')
+
+if __name__ == '__main__':
+    unittest2.main()
diff --git a/openerp/addons/base/tests/test_mail_examples.py b/openerp/addons/base/tests/test_mail_examples.py
new file mode 100644 (file)
index 0000000..6886ce8
--- /dev/null
@@ -0,0 +1,1106 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+MISC_HTML_SOURCE = """
+<font size="2" style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; ">test1</font>
+<div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; font-size: 12px; font-style: normal; ">
+<b>test2</b></div><div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; font-size: 12px; ">
+<i>test3</i></div><div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; font-size: 12px; ">
+<u>test4</u></div><div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; font-size: 12px; ">
+<strike>test5</strike></div><div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; ">
+<font size="5">test6</font></div><div><ul><li><font color="#1f1f1f" face="monospace" size="2">test7</font></li><li>
+<font color="#1f1f1f" face="monospace" size="2">test8</font></li></ul><div><ol><li><font color="#1f1f1f" face="monospace" size="2">test9</font>
+</li><li><font color="#1f1f1f" face="monospace" size="2">test10</font></li></ol></div></div>
+<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><div><div><div><font color="#1f1f1f" face="monospace" size="2">
+test11</font></div></div></div></blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">
+<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><div><font color="#1f1f1f" face="monospace" size="2">
+test12</font></div><div><font color="#1f1f1f" face="monospace" size="2"><br></font></div></blockquote></blockquote>
+<font color="#1f1f1f" face="monospace" size="2"><a href="http://google.com">google</a></font>
+<a href="javascript:alert('malicious code')">test link</a>
+"""
+
+EDI_LIKE_HTML_SOURCE = """<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
+    <p>Hello ${object.partner_id.name},</p>
+    <p>A new invoice is available for you: </p>
+    <p style="border-left: 1px solid #8e0000; margin-left: 30px;">
+       &nbsp;&nbsp;<strong>REFERENCES</strong><br />
+       &nbsp;&nbsp;Invoice number: <strong>${object.number}</strong><br />
+       &nbsp;&nbsp;Invoice total: <strong>${object.amount_total} ${object.currency_id.name}</strong><br />
+       &nbsp;&nbsp;Invoice date: ${object.date_invoice}<br />
+       &nbsp;&nbsp;Order reference: ${object.origin}<br />
+       &nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a>
+    </p>
+    <br/>
+    <p>It is also possible to directly pay with Paypal:</p>
+    <a style="margin-left: 120px;" href="${object.paypal_url}">
+        <img class="oe_edi_paypal_button" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"/>
+    </a>
+    <br/>
+    <p>If you have any question, do not hesitate to contact us.</p>
+    <p>Thank you for choosing ${object.company_id.name or 'us'}!</p>
+    <br/>
+    <br/>
+    <div style="width: 375px; margin: 0px; padding: 0px; background-color: #8E0000; border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; background-repeat: repeat no-repeat;">
+        <h3 style="margin: 0px; padding: 2px 14px; font-size: 12px; color: #DDD;">
+            <strong style="text-transform:uppercase;">${object.company_id.name}</strong></h3>
+    </div>
+    <div style="width: 347px; margin: 0px; padding: 5px 14px; line-height: 16px; background-color: #F2F2F2;">
+        <span style="color: #222; margin-bottom: 5px; display: block; ">
+        ${object.company_id.street}<br/>
+        ${object.company_id.street2}<br/>
+        ${object.company_id.zip} ${object.company_id.city}<br/>
+        ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}<br/>
+        </span>
+        <div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
+            Phone:&nbsp; ${object.company_id.phone}
+        </div>
+        <div>
+            Web :&nbsp;<a href="${object.company_id.website}">${object.company_id.website}</a>
+        </div>
+    </div>
+</div></body></html>"""
+
+OERP_WEBSITE_HTML_1 = """
+<div>
+    <div class="container">
+        <div class="row">
+            <div class="col-md-12 text-center mt16 mb16" data-snippet-id="colmd">
+                <h2>OpenERP HR Features</h2>
+                <h3 class="text-muted">Manage your company most important asset: People</h3>
+            </div>
+            <div class="col-md-4" data-snippet-id="colmd">
+                <img class="img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg">
+                <h4 class="mt16">Streamline Recruitments</h4>
+                <p>Post job offers and keep track of each application received. Follow applicants in your recruitment process with the smart kanban view.</p>
+                <p>Save time by automating some communications with email templates. Resumes are indexed automatically, allowing you to easily find for specific profiles.</p>
+            </div>
+            <div class="col-md-4" data-snippet-id="colmd">
+                <img class="img-rounded img-responsive" src="/website/static/src/img/desert_thumb.jpg">
+                <h4 class="mt16">Enterprise Social Network</h4>
+                <p>Break down information silos. Share knowledge and best practices amongst all employees. Follow specific people or documents and join groups of interests to share expertise and documents.</p>
+                <p>Interact with your collegues in real time with live chat.</p>
+            </div>
+            <div class="col-md-4" data-snippet-id="colmd">
+                <img class="img-rounded img-responsive" src="/website/static/src/img/deers_thumb.jpg">
+                <h4 class="mt16">Leaves Management</h4>
+                <p>Keep track of the vacation days accrued by each employee. Employees enter their requests (paid holidays, sick leave, etc), for managers to approve and validate. It's all done in just a few clicks. The agenda of each employee is updated accordingly.</p>
+            </div>
+        </div>
+    </div>
+</div>"""
+
+OERP_WEBSITE_HTML_1_IN = [
+    'Manage your company most important asset: People',
+    'img class="img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg"',
+]
+OERP_WEBSITE_HTML_1_OUT = [
+    'Break down information silos.',
+    'Keep track of the vacation days accrued by each employee',
+    'img class="img-rounded img-responsive" src="/website/static/src/img/deers_thumb.jpg',
+]
+
+OERP_WEBSITE_HTML_2 = """
+<div class="mt16 cke_widget_editable cke_widget_element oe_editable oe_dirty" data-oe-model="blog.post" data-oe-id="6" data-oe-field="content" data-oe-type="html" data-oe-translate="0" data-oe-expression="blog_post.content" data-cke-widget-data="{}" data-cke-widget-keep-attr="0" data-widget="oeref" contenteditable="true" data-cke-widget-editable="text">
+    <section class="mt16 mb16" data-snippet-id="text-block">
+        <div class="container">
+            <div class="row">
+                <div class="col-md-12 text-center mt16 mb32" data-snippet-id="colmd">
+                    <h2>
+                        OpenERP Project Management
+                    </h2>
+                    <h3 class="text-muted">Infinitely flexible. Incredibly easy to use.</h3>
+                </div>
+                <div class="col-md-12 mb16 mt16" data-snippet-id="colmd">
+                    <p>
+                        OpenERP's <b>collaborative and realtime</b> project
+                        management helps your team get work done. Keep
+                        track of everything, from the big picture to the
+                        minute details, from the customer contract to the
+                        billing.
+                    </p><p>
+                        Organize projects around <b>your own processes</b>. Work
+                        on tasks and issues using the kanban view, schedule
+                        tasks using the gantt chart and control deadlines
+                        in the calendar view. Every project may have it's
+                        own stages allowing teams to optimize their job.
+                    </p>
+                </div>
+            </div>
+        </div>
+    </section>
+    <section class="" data-snippet-id="image-text">
+        <div class="container">
+            <div class="row">
+                <div class="col-md-6 mt16 mb16" data-snippet-id="colmd">
+                    <img class="img-responsive shadow" src="/website/static/src/img/image_text.jpg">
+                </div>
+                <div class="col-md-6 mt32" data-snippet-id="colmd">
+                    <h3>Manage Your Shops</h3>
+                    <p>
+                        OpenERP's Point of Sale introduces a super clean
+                        interface with no installation required that runs
+                        online and offline on modern hardwares.
+                    </p><p>
+                        It's full integration with the company inventory
+                        and accounting, gives you real time statistics and
+                        consolidations amongst all shops without the hassle
+                        of integrating several applications.
+                    </p>
+                </div>
+            </div>
+        </div>
+    </section>
+    <section class="" data-snippet-id="text-image">
+        <div class="container">
+            <div class="row">
+                <div class="col-md-6 mt32" data-snippet-id="colmd">
+                    <h3>Enterprise Social Network</h3>
+                    <p>
+                        Make every employee feel more connected and engaged
+                        with twitter-like features for your own company. Follow
+                        people, share best practices, 'like' top ideas, etc.
+                    </p><p>
+                        Connect with experts, follow what interests you, share
+                        documents and promote best practices with OpenERP
+                        Social application. Get work done with effective
+                        collaboration across departments, geographies
+                        and business applications.
+                    </p>
+                </div>
+                <div class="col-md-6 mt16 mb16" data-snippet-id="colmd">
+                    <img class="img-responsive shadow" src="/website/static/src/img/text_image.png">
+                </div>
+            </div>
+        </div>
+    </section><section class="" data-snippet-id="portfolio">
+        <div class="container">
+            <div class="row">
+                <div class="col-md-12 text-center mt16 mb32" data-snippet-id="colmd">
+                    <h2>Our Porfolio</h2>
+                    <h4 class="text-muted">More than 500 successful projects</h4>
+                </div>
+                <div class="col-md-4" data-snippet-id="colmd">
+                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/deers.jpg">
+                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg">
+                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg">
+                </div>
+                <div class="col-md-4" data-snippet-id="colmd">
+                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg">
+                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg">
+                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/deers.jpg">
+                </div>
+                <div class="col-md-4" data-snippet-id="colmd">
+                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/landscape.jpg">
+                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg">
+                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg">
+                </div>
+            </div>
+        </div>
+    </section>
+</div>
+"""
+
+OERP_WEBSITE_HTML_2_IN = [
+    'management helps your team get work done',
+]
+OERP_WEBSITE_HTML_2_OUT = [
+    'Make every employee feel more connected',
+    'img class="img-responsive shadow" src="/website/static/src/img/text_image.png',
+]
+
+TEXT_1 = """I contact you about our meeting tomorrow. Here is the schedule I propose:
+9 AM: brainstorming about our new amazing business app
+9.45 AM: summary
+10 AM: meeting with Ignasse to present our app
+Is everything ok for you ?
+--
+MySignature"""
+
+TEXT_1_IN = ["""I contact you about our meeting tomorrow. Here is the schedule I propose:
+9 AM: brainstorming about our new amazing business app
+9.45 AM: summary
+10 AM: meeting with Ignasse to present our app
+Is everything ok for you ?"""]
+TEXT_1_OUT = ["""--
+MySignature"""]
+
+TEXT_2 = """Salut Raoul!
+Le 28 oct. 2012 à 00:02, Raoul Grosbedon a écrit :
+
+> I contact you about our meeting tomorrow. Here is the schedule I propose: (quote)
+
+Of course. This seems viable.
+
+> 2012/10/27 Bert Tartopoils :
+>> blahblahblah (quote)?
+>> 
+>> blahblahblah (quote)
+>> 
+>> Bert TARTOPOILS
+>> bert.tartopoils@miam.miam
+>> 
+> 
+> 
+> -- 
+> RaoulSignature
+
+Bert TARTOPOILS
+bert.tartopoils@miam.miam
+"""
+
+TEXT_2_IN = ["Salut Raoul!", "Of course. This seems viable."]
+TEXT_2_OUT = ["I contact you about our meeting tomorrow. Here is the schedule I propose: (quote)",
+    """> 2012/10/27 Bert Tartopoils :
+>> blahblahblah (quote)?
+>> 
+>> blahblahblah (quote)
+>> 
+>> Bert TARTOPOILS
+>> bert.tartopoils@miam.miam
+>> 
+> 
+> 
+> -- 
+> RaoulSignature"""]
+
+HTML_1 = """<p>I contact you about our meeting for tomorrow. Here is the schedule I propose: (keep)
+9 AM: brainstorming about our new amazing business app
+9.45 AM: summary
+10 AM: meeting with Ignasse to present our app
+Is everything ok for you ?
+--
+MySignature</p>"""
+
+HTML_1_IN = ["""I contact you about our meeting for tomorrow. Here is the schedule I propose: (keep)
+9 AM: brainstorming about our new amazing business app
+9.45 AM: summary
+10 AM: meeting with Ignasse to present our app
+Is everything ok for you ?"""]
+HTML_1_OUT = ["""--
+MySignature"""]
+
+HTML_2 = """<div>
+    <font><span>I contact you about our meeting for tomorrow. Here is the schedule I propose:</span></font>
+</div>
+<div>
+    <ul>
+        <li><span>9 AM: brainstorming about our new amazing business app</span></li>
+        <li><span>9.45 AM: summary</span></li>
+        <li><span>10 AM: meeting with Fabien to present our app</span></li>
+    </ul>
+</div>
+<div>
+    <font><span>Is everything ok for you ?</span></font>
+</div>"""
+
+HTML_2_IN = ["<font><span>I contact you about our meeting for tomorrow. Here is the schedule I propose:</span></font>",
+    "<li><span>9 AM: brainstorming about our new amazing business app</span></li>",
+    "<li><span>9.45 AM: summary</span></li>",
+    "<li><span>10 AM: meeting with Fabien to present our app</span></li>",
+    "<font><span>Is everything ok for you ?</span></font>"]
+HTML_2_OUT = []
+
+HTML_3 = """<div><pre>This is an answer.
+
+Regards,
+XXXXXX
+----- Mail original -----</pre>
+
+
+<pre>Hi, 
+
+
+My CRM-related question.
+
+Regards, 
+
+XXXX</pre></div>"""
+
+HTML_3_IN = ["""<div><pre>This is an answer.
+
+Regards,
+XXXXXX
+----- Mail original -----</pre>"""]
+HTML_3_OUT = ["Hi,", "My CRM-related question.",
+    "Regards,"]
+
+HTML_4 = """
+<div>
+    <div>Hi Nicholas,</div>
+    <br>
+    <div>I'm free now. 00447710085916.</div>
+    <br>
+    <div>Regards,</div>
+    <div>Nicholas</div>
+    <br>
+    <span id="OLK_SRC_BODY_SECTION">
+        <div style="font-family:Calibri; font-size:11pt; text-align:left; color:black; BORDER-BOTTOM: medium none; BORDER-LEFT: medium none; PADDING-BOTTOM: 0in; PADDING-LEFT: 0in; PADDING-RIGHT: 0in; BORDER-TOP: #b5c4df 1pt solid; BORDER-RIGHT: medium none; PADDING-TOP: 3pt">
+            <span style="font-weight:bold">From: </span>OpenERP Enterprise &lt;<a href="mailto:sales@openerp.com">sales@openerp.com</a>&gt;<br><span style="font-weight:bold">Reply-To: </span>&lt;<a href="mailto:sales@openerp.com">sales@openerp.com</a>&gt;<br><span style="font-weight:bold">Date: </span>Wed, 17 Apr 2013 13:30:47 +0000<br><span style="font-weight:bold">To: </span>Microsoft Office User &lt;<a href="mailto:n.saxlund@babydino.com">n.saxlund@babydino.com</a>&gt;<br><span style="font-weight:bold">Subject: </span>Re: your OpenERP.com registration<br>
+        </div>
+        <br>
+        <div>
+            <p>Hello Nicholas Saxlund, </p>
+            <p>I noticed you recently registered to our OpenERP Online solution. </p>
+            <p>You indicated that you wish to use OpenERP in your own company. We would like to know more about your your business needs and requirements, and see how we can help you. When would you be available to discuss your project ?
+            </p>
+            <p>Best regards, </p>
+            <pre><a href="http://openerp.com">http://openerp.com</a>
+Belgium: +32.81.81.37.00
+U.S.: +1 (650) 307-6736
+India: +91 (79) 40 500 100
+                        </pre>
+        </div>
+    </span>
+</div>"""
+
+HTML_5 = """<div><pre>Hi,
+
+I have downloaded OpenERP installer 7.0 and successfully installed the postgresql server and the OpenERP.
+I created a database and started to install module by log in as administrator.
+However, I was not able to install any module due to "OpenERP Server Error" as shown in the attachement.
+Could you please let me know how could I fix this problem?
+
+&nbsp;Regards,
+Goh Sin Yih
+
+
+________________________________
+ From: OpenERP Enterprise &lt;sales@openerp.com&gt;
+To: sinyih_goh@yahoo.com 
+Sent: Friday, February 8, 2013 12:46 AM
+Subject: Feedback From Your OpenERP Trial
+
+Hello Goh Sin Yih, 
+Thank you for having tested OpenERP Online. 
+I noticed you started a trial of OpenERP Online (gsy) but you did not decide to keep using it. 
+So, I just wanted to get in touch with you to get your feedback. Can you tell me what kind of application you were you looking for and why you didn't decide to continue with OpenERP? 
+Thanks in advance for providing your feedback, 
+Do not hesitate to contact me if you have any questions, 
+Thanks, 
+</pre>"""
+
+GMAIL_1 = """Hello,<div><br></div><div>Ok for me. I am replying directly in gmail, without signature.</div><div><br></div><div>Kind regards,</div><div><br></div><div>Demo.<br><br><div>On Thu, Nov 8, 2012 at 5:29 PM,  <span>&lt;<a href="mailto:dummy@example.com">dummy@example.com</a>&gt;</span> wrote:<br><blockquote><div>I contact you about our meeting for tomorrow. Here is the schedule I propose:</div><div><ul><li>9 AM: brainstorming about our new amazing business app&lt;/span&gt;&lt;/li&gt;</li>
+<li>9.45 AM: summary</li><li>10 AM: meeting with Fabien to present our app</li></ul></div><div>Is everything ok for you ?</div>
+<div><p>--<br>Administrator</p></div>
+
+<div><p>Log in our portal at: <a href="http://localhost:8069#action=login&amp;db=mail_1&amp;login=demo">http://localhost:8069#action=login&amp;db=mail_1&amp;login=demo</a></p></div>
+</blockquote></div><br></div>"""
+
+GMAIL_1_IN = ['Ok for me. I am replying directly in gmail, without signature.']
+GMAIL_1_OUT = ['Administrator', 'Log in our portal at:']
+
+THUNDERBIRD_1 = """<div>On 11/08/2012 05:29 PM,
+      <a href="mailto:dummy@example.com">dummy@example.com</a> wrote:<br></div>
+    <blockquote>
+      <div>I contact you about our meeting for tomorrow. Here is the
+        schedule I propose:</div>
+      <div>
+        <ul><li>9 AM: brainstorming about our new amazing business
+            app&lt;/span&gt;&lt;/li&gt;</li>
+          <li>9.45 AM: summary</li>
+          <li>10 AM: meeting with Fabien to present our app</li>
+        </ul></div>
+      <div>Is everything ok for you ?</div>
+      <div>
+        <p>--<br>
+          Administrator</p>
+      </div>
+      <div>
+        <p>Log in our portal at:
+<a href="http://localhost:8069#action=login&amp;db=mail_1&amp;token=rHdWcUART5PhEnJRaXjH">http://localhost:8069#action=login&amp;db=mail_1&amp;token=rHdWcUART5PhEnJRaXjH</a></p>
+      </div>
+    </blockquote>
+    Ok for me. I am replying directly below your mail, using Thunderbird, with a signature.<br><br>
+    Did you receive my email about my new laptop, by the way ?<br><br>
+    Raoul.<br><pre>-- 
+Raoul Grosbedonn&#233;e
+</pre>"""
+
+THUNDERBIRD_1_IN = ['Ok for me. I am replying directly below your mail, using Thunderbird, with a signature.']
+THUNDERBIRD_1_OUT = ['I contact you about our meeting for tomorrow.', 'Raoul Grosbedon']
+
+HOTMAIL_1 = """<div>
+    <div dir="ltr"><br>&nbsp;
+        I have an amazing company, i'm learning OpenERP, it is a small company yet, but plannig to grow up quickly.
+        <br>&nbsp;<br>Kindest regards,<br>xxx<br>
+        <div>
+            <div id="SkyDrivePlaceholder">
+            </div>
+            <hr id="stopSpelling">
+            Subject: Re: your OpenERP.com registration<br>From: xxx@xxx.xxx<br>To: xxx@xxx.xxx<br>Date: Wed, 27 Mar 2013 17:12:12 +0000
+            <br><br>
+            Hello xxx,
+            <br>
+            I noticed you recently created an OpenERP.com account to access OpenERP Apps.
+            <br>
+            You indicated that you wish to use OpenERP in your own company.
+            We would like to know more about your your business needs and requirements, and see how
+            we can help you. When would you be available to discuss your project ?<br>
+            Best regards,<br>
+            <pre>
+                <a href="http://openerp.com" target="_blank">http://openerp.com</a>
+                Belgium: +32.81.81.37.00
+                U.S.: +1 (650) 307-6736
+                India: +91 (79) 40 500 100
+            </pre>
+        </div>
+    </div>
+</div>"""
+
+HOTMAIL_1_IN = ["I have an amazing company, i'm learning OpenERP, it is a small company yet, but plannig to grow up quickly."]
+HOTMAIL_1_OUT = ["Subject: Re: your OpenERP.com registration", " I noticed you recently created an OpenERP.com account to access OpenERP Apps.",
+    "We would like to know more about your your business needs and requirements", "Belgium: +32.81.81.37.00"]
+
+MSOFFICE_1 = """
+<div>
+<div class="WordSection1">
+        <p class="MsoNormal">
+            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+                Our requirements are simple. Just looking to replace some spreadsheets for tracking quotes and possibly using the timecard module.
+                We are a company of 25 engineers providing product design services to clients.
+            </span>
+        </p>
+        <p></p>
+        <p></p>
+        <p class="MsoNormal">
+            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+                I’ll install on a windows server and run a very limited trial to see how it works.
+                If we adopt OpenERP we will probably move to Linux or look for a hosted SaaS option.
+            </span>
+        </p>
+        <p></p>
+        <p></p>
+        <p class="MsoNormal">
+            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+                <br>
+                I am also evaluating Adempiere and maybe others.
+            </span>
+        </p>
+        <p></p>
+        <p></p>
+        <p class="MsoNormal">
+            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+            </span>
+        </p>
+        <p>&nbsp;</p>
+        <p></p>
+        <p class="MsoNormal">
+            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+                I expect the trial will take 2-3 months as this is not a high priority for us.
+            </span>
+        </p>
+        <p></p>
+        <p></p>
+        <p class="MsoNormal">
+            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+            </span>
+        </p>
+        <p>&nbsp;</p>
+        <p></p>
+        <p class="MsoNormal">
+            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+                Alan
+            </span>
+        </p>
+        <p></p>
+        <p></p>
+        <p class="MsoNormal">
+            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+            </span>
+        </p>
+        <p>&nbsp;</p>
+        <p></p>
+        <div>
+            <div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
+                <p class="MsoNormal">
+                    <b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">
+                        From:
+                    </span></b>
+                    <span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">
+                        OpenERP Enterprise [mailto:sales@openerp.com]
+                        <br><b>Sent:</b> Monday, 11 March, 2013 14:47<br><b>To:</b> Alan Widmer<br><b>Subject:</b> Re: your OpenERP.com registration
+                    </span>
+                </p>
+                <p></p>
+                <p></p>
+            </div>
+        </div>
+        <p class="MsoNormal"></p>
+        <p>&nbsp;</p>
+        <p>Hello Alan Widmer, </p>
+        <p></p>
+        <p>I noticed you recently downloaded OpenERP. </p>
+        <p></p>
+        <p>
+            Uou mentioned you wish to use OpenERP in your own company. Please let me more about your
+            business needs and requirements? When will you be available to discuss about your project?
+        </p>
+        <p></p>
+        <p>Thanks for your interest in OpenERP, </p>
+        <p></p>
+        <p>Feel free to contact me if you have any questions, </p>
+        <p></p>
+        <p>Looking forward to hear from you soon. </p>
+        <p></p>
+        <pre><p>&nbsp;</p></pre>
+        <pre>--<p></p></pre>
+        <pre>Nicolas<p></p></pre>
+        <pre><a href="http://openerp.com">http://openerp.com</a><p></p></pre>
+        <pre>Belgium: +32.81.81.37.00<p></p></pre>
+        <pre>U.S.: +1 (650) 307-6736<p></p></pre>
+        <pre>India: +91 (79) 40 500 100<p></p></pre>
+        <pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<p></p></pre>
+    </div>
+</div>"""
+
+MSOFFICE_1_IN = ['Our requirements are simple. Just looking to replace some spreadsheets for tracking quotes and possibly using the timecard module.']
+MSOFFICE_1_OUT = ['I noticed you recently downloaded OpenERP.', 'Uou mentioned you wish to use OpenERP in your own company.', 'Belgium: +32.81.81.37.00']
+
+MSOFFICE_2 = """
+<div>
+  <div class="WordSection1">
+    <p class="MsoNormal">
+      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Nicolas,</span></p><p></p>
+    <p></p>
+    <p class="MsoNormal" style="text-indent:.5in">
+      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">We are currently investigating the possibility of moving away from our current ERP </span></p><p></p>
+    <p></p>
+    <p class="MsoNormal">
+      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+        </span></p><p>&nbsp;</p>
+      
+    <p></p>
+    <p class="MsoNormal">
+      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Thank You</span></p><p></p>
+    <p></p>
+    <p class="MsoNormal">
+      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Matt</span></p><p></p>
+    <p></p>
+    <p class="MsoNormal">
+      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+        </span></p><p>&nbsp;</p>
+      
+    <p></p>
+    <div>
+      <p class="MsoNormal">
+        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Raoul Petitpoil</span></p><p></p>
+      <p></p>
+      <p class="MsoNormal">
+        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Poil Industries</span></p><p></p>
+      <p></p>
+      <p class="MsoNormal">
+        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Information Technology</span></p><p></p>
+      <p></p>
+      <p class="MsoNormal">
+        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">920 Super Street</span></p><p></p>
+      <p></p>
+      <p class="MsoNormal">
+        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Sanchez, Pa 17046 USA</span></p><p></p>
+      <p></p>
+      <p class="MsoNormal">
+        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Tel: xxx.xxx</span></p><p></p>
+      <p></p>
+      <p class="MsoNormal">
+        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Fax: xxx.xxx</span></p><p></p>
+      <p></p>
+      <p class="MsoNormal">
+        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Email: </span>
+        <a href="mailto:raoul@petitpoil.com">
+          <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:blue">raoul@petitpoil.com</span>
+        </a>
+        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+          </span></p><p></p>
+        
+      <p></p>
+      <p class="MsoNormal">
+        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">www.poilindustries.com</span></p><p></p>
+      <p></p>
+      <p class="MsoNormal">
+        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">www.superproducts.com</span></p><p></p>
+      <p></p>
+    </div>
+    <p class="MsoNormal">
+      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+        </span></p><p>&nbsp;</p>
+      
+    <p></p>
+    <div>
+      <div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
+        <p class="MsoNormal">
+          <b>
+            <span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">From:</span>
+          </b>
+          <span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;"> OpenERP Enterprise [mailto:sales@openerp.com] <br><b>Sent:</b> Wednesday, April 17, 2013 1:31 PM<br><b>To:</b> Matt Witters<br><b>Subject:</b> Re: your OpenERP.com registration</span></p><p></p>
+        <p></p>
+      </div>
+    </div>
+    <p class="MsoNormal"></p>
+    <p>&nbsp;</p>
+    <p>Hello Raoul Petitpoil, </p>
+    <p></p>
+    <p>I noticed you recently downloaded OpenERP. </p>
+    <p></p>
+    <p>You indicated that you wish to use OpenERP in your own company. We would like to know more about your your business needs and requirements, and see how we can help you. When would you be available to discuss your project ? </p>
+    <p></p>
+    <p>Best regards, </p>
+    <p></p>
+    <pre>      <p>&nbsp;</p>
+    </pre>
+    <pre>--<p></p></pre>
+    <pre>Nicolas<p></p></pre>
+    <pre>      <a href="http://openerp.com">http://openerp.com</a>
+      <p></p>
+    </pre>
+    <pre>Belgium: +32.81.81.37.00<p></p></pre>
+    <pre>U.S.: +1 (650) 307-6736<p></p></pre>
+    <pre>India: +91 (79) 40 500 100<p></p></pre>
+    <pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <p></p></pre>
+  </div>
+</div>"""
+
+MSOFFICE_2_IN = ['We are currently investigating the possibility']
+MSOFFICE_2_OUT = ['I noticed you recently downloaded OpenERP.', 'You indicated that you wish', 'Belgium: +32.81.81.37.00']
+
+MSOFFICE_3 = """<div>
+  <div class="WordSection1">
+    <p class="MsoNormal">
+      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Hi Nicolas&nbsp;!</span></p><p></p>
+    <p></p>
+    <p class="MsoNormal">
+      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+        </span></p><p>&nbsp;</p>
+      
+    <p></p>
+    <p class="MsoNormal">
+      <span lang="EN-US" style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Yes I’d be glad to hear about your offers as we struggle every year with the planning/approving of LOA. </span></p><p></p>
+    <p></p>
+    <p class="MsoNormal">
+      <span lang="EN-US" style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">I saw your boss yesterday on tv and immediately wanted to test the interface. </span></p><p></p>
+    <p></p>
+    <p class="MsoNormal">
+      <span lang="EN-US" style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+        </span></p><p>&nbsp;</p>
+
+    <p></p>
+    <div>
+      <p class="MsoNormal">
+        <b>
+          <span lang="NL-BE" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">Bien à vous, </span></b></p><p></p><b>
+        </b>
+      <p></p>
+      <p class="MsoNormal">
+        <b>
+          <span lang="NL-BE" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">Met vriendelijke groeten, </span></b></p><p></p><b>
+        </b>
+      <p></p>
+      <p class="MsoNormal">
+        <b>
+          <span lang="EN-GB" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">Best regards,</span></b></p><p></p><b>
+        </b>
+      <p></p>
+      <p class="MsoNormal">
+        <b>
+          <span lang="EN-GB" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">
+            </span></b></p><p><b>&nbsp;</b></p><b>
+          
+        </b>
+      <p></p>
+      <p class="MsoNormal">
+        <b>
+          <span lang="EN-GB" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">R. Petitpoil&nbsp;&nbsp;&nbsp; <br></span>
+        </b>
+        <span lang="EN-GB" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">Human Resource Manager<b><br><br>Field Resource s.a n.v.&nbsp;&nbsp;<i> <br></i></b>Hermesstraat 6A <br>1930 Zaventem</span>
+        <span lang="EN-GB" style="font-size:8.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;;color:gray"><br></span>
+        <b>
+          <span lang="FR" style="font-size:10.0pt;font-family:Wingdings;color:#1F497D">(</span>
+        </b>
+        <b>
+          <span lang="FR" style="font-size:9.0pt;font-family:Wingdings;color:#1F497D"> </span>
+        </b>
+        <b>
+          <span lang="EN-GB" style="font-size:8.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">xxx.xxx &nbsp;</span>
+        </b>
+        <b>
+          <span lang="EN-GB" style="font-size:9.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray"><br></span>
+        </b>
+        <b>
+          <span lang="FR" style="font-size:10.0pt;font-family:&quot;Wingdings 2&quot;;color:#1F497D">7</span>
+        </b>
+        <b>
+          <span lang="FR" style="font-size:9.0pt;font-family:&quot;Wingdings 2&quot;;color:#1F497D"> </span>
+        </b>
+        <b>
+          <span lang="EN-GB" style="font-size:8.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">+32 2 727.05.91<br></span>
+        </b>
+        <span lang="EN-GB" style="font-size:24.0pt;font-family:Webdings;color:green">P</span>
+        <span lang="EN-GB" style="font-size:8.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;;color:green"> <b>&nbsp;&nbsp; </b></span>
+        <b>
+          <span lang="EN-GB" style="font-size:9.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:green">Please consider the environment before printing this email.</span>
+        </b>
+        <span lang="EN-GB" style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:navy"> </span>
+        <span lang="EN-GB" style="font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:navy">
+          </span></p><p></p>
+        
+      <p></p>
+    </div>
+    <p class="MsoNormal">
+      <span lang="EN-US" style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
+        </span></p><p>&nbsp;</p>
+      
+    <p></p>
+    <div>
+      <div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm">
+        <p class="MsoNormal">
+          <b>
+            <span lang="FR" style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">De&nbsp;:</span>
+          </b>
+          <span lang="FR" style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;"> OpenERP Enterprise [mailto:sales@openerp.com] <br><b>Envoyé&nbsp;:</b> jeudi 18 avril 2013 11:31<br><b>À&nbsp;:</b> Paul Richard<br><b>Objet&nbsp;:</b> Re: your OpenERP.com registration</span></p><p></p>
+        <p></p>
+      </div>
+    </div>
+    <p class="MsoNormal"></p>
+    <p>&nbsp;</p>
+    <p>Hello Raoul PETITPOIL, </p>
+    <p></p>
+    <p>I noticed you recently registered to our OpenERP Online solution. </p>
+    <p></p>
+    <p>You indicated that you wish to use OpenERP in your own company. We would like to know more about your your business needs and requirements, and see how we can help you. When would you be available to discuss your project ? </p>
+    <p></p>
+    <p>Best regards, </p>
+    <p></p>
+    <pre>      <p>&nbsp;</p>
+    </pre>
+    <pre>--<p></p></pre>
+    <pre>Nicolas<p></p></pre>
+    <pre>      <a href="http://openerp.com">http://openerp.com</a>
+      <p></p>
+    </pre>
+    <pre>Belgium: +32.81.81.37.00<p></p></pre>
+    <pre>U.S.: +1 (650) 307-6736<p></p></pre>
+    <pre>India: +91 (79) 40 500 100<p></p></pre>
+    <pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <p></p></pre>
+  </div>
+</div>"""
+
+MSOFFICE_3_IN = ['I saw your boss yesterday']
+MSOFFICE_3_OUT = ['I noticed you recently downloaded OpenERP.', 'You indicated that you wish', 'Belgium: +32.81.81.37.00']
+
+
+# ------------------------------------------------------------
+# Test cases coming from bugs
+# ------------------------------------------------------------
+
+# bug: read more not apparent, strange message in read more span
+BUG1 = """<pre>Hi Migration Team,
+
+Paragraph 1, blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah.
+
+Paragraph 2, blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah.
+
+Paragraph 3, blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
+blah blah blah blah blah blah blah blah.
+
+Thanks.
+
+Regards,
+
+-- 
+Olivier Laurent
+Migration Manager
+OpenERP SA
+Chaussée de Namur, 40
+B-1367 Gérompont
+Tel: +32.81.81.37.00
+Web: http://www.openerp.com</pre>"""
+
+BUG_1_IN = [
+    'Hi Migration Team',
+    'Paragraph 1'
+]
+BUG_1_OUT = [
+    'Olivier Laurent',
+    'Chaussée de Namur',
+    '81.81.37.00',
+    'openerp.com',
+]
+
+
+BUG2 = """
+<div>
+    <br>
+    <div class="moz-forward-container"><br>
+      <br>
+      -------- Original Message --------
+      <table class="moz-email-headers-table" border="0" cellpadding="0" cellspacing="0">
+        <tbody>
+          <tr>
+            <th nowrap="" valign="BASELINE" align="RIGHT">Subject:
+            </th>
+            <td>Fwd: TR: OpenERP S.A. Payment Reminder</td>
+          </tr>
+          <tr>
+            <th nowrap="" valign="BASELINE" align="RIGHT">Date: </th>
+            <td>Wed, 16 Oct 2013 14:11:13 +0200</td>
+          </tr>
+          <tr>
+            <th nowrap="" valign="BASELINE" align="RIGHT">From: </th>
+            <td>Christine Herrmann <a class="moz-txt-link-rfc2396E" href="mailto:che@openerp.com">&lt;che@openerp.com&gt;</a></td>
+          </tr>
+          <tr>
+            <th nowrap="" valign="BASELINE" align="RIGHT">To: </th>
+            <td><a class="moz-txt-link-abbreviated" href="mailto:online@openerp.com">online@openerp.com</a></td>
+          </tr>
+        </tbody>
+      </table>
+      <br>
+      <br>
+      
+      <br>
+      <div class="moz-forward-container"><br>
+        <br>
+        -------- Message original --------
+        <table class="moz-email-headers-table" border="0" cellpadding="0" cellspacing="0">
+          <tbody>
+            <tr>
+              <th nowrap="" valign="BASELINE" align="RIGHT">Sujet:
+              </th>
+              <td>TR: OpenERP S.A. Payment Reminder</td>
+            </tr>
+            <tr>
+              <th nowrap="" valign="BASELINE" align="RIGHT">Date&nbsp;:
+              </th>
+              <td>Wed, 16 Oct 2013 10:34:45 -0000</td>
+            </tr>
+            <tr>
+              <th nowrap="" valign="BASELINE" align="RIGHT">De&nbsp;: </th>
+              <td>Ida Siwatala <a class="moz-txt-link-rfc2396E" href="mailto:infos@inzoservices.com">&lt;infos@inzoservices.com&gt;</a></td>
+            </tr>
+            <tr>
+              <th nowrap="" valign="BASELINE" align="RIGHT">Répondre
+
+                à&nbsp;: </th>
+              <td><a class="moz-txt-link-abbreviated" href="mailto:catchall@openerp.my.openerp.com">catchall@openerp.my.openerp.com</a></td>
+            </tr>
+            <tr>
+              <th nowrap="" valign="BASELINE" align="RIGHT">Pour&nbsp;:
+              </th>
+              <td>Christine Herrmann (che) <a class="moz-txt-link-rfc2396E" href="mailto:che@openerp.com">&lt;che@openerp.com&gt;</a></td>
+            </tr>
+          </tbody>
+        </table>
+        <br>
+        <br>
+        <div>
+          <div class="WordSection1">
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Bonjour,</span></p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
+            <p>&nbsp;</p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Pourriez-vous
+
+                me faire un retour sur ce point.</span></p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
+            <p>&nbsp;</p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Cordialement</span></p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
+            <p>&nbsp;</p>
+            <div>
+              <div style="border:none;border-top:solid #B5C4DF
+                1.0pt;padding:3.0pt 0cm 0cm 0cm">
+                <p class="MsoNormal"><b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">De&nbsp;:</span></b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">
+                    Ida Siwatala [<a class="moz-txt-link-freetext" href="mailto:infos@inzoservices.com">mailto:infos@inzoservices.com</a>]
+                    <br>
+                    <b>Envoyé&nbsp;:</b> vendredi 4 octobre 2013 20:03<br>
+                    <b>À&nbsp;:</b> 'Followers of
+                    INZO-services-8-all-e-Maxime-Lisbonne-77176-Savigny-le-temple-France'<br>
+                    <b>Objet&nbsp;:</b> RE: OpenERP S.A. Payment Reminder</span></p>
+              </div>
+            </div>
+            <p>&nbsp;</p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Bonsoir,</span></p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
+            <p>&nbsp;</p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Je
+
+                me permets de revenir vers vous par écrit , car j’ai
+                fait 2 appels vers votre service en exposant mon
+                problème, mais je n’ai pas eu de retour.</span></p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Cela
+
+                fait un mois que j’ai fait la souscription de votre
+                produit, mais je me rends compte qu’il est pas adapté à
+                ma situation ( fonctionnalité manquante et surtout je
+                n’ai pas beaucoup de temps à passer à résoudre des
+                bugs). </span></p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">C’est
+
+                pourquoi , j’ai demandé qu’un accord soit trouvé avec
+                vous pour annuler le contrat (tout en vous payant le
+                mois d’utilisation de septembre).</span></p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
+            <p>&nbsp;</p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Pourriez-vous
+
+                me faire un retour sur ce point.</span></p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
+            <p>&nbsp;</p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Cordialement,</span></p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
+            <p>&nbsp;</p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Ida
+
+                Siwatala</span></p>
+            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
+            <p>&nbsp;</p>
+            <p class="MsoNormal"><b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">De&nbsp;:</span></b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">
+                <a href="mailto:che@openerp.com">che@openerp.com</a>
+                [<a href="mailto:che@openerp.com">mailto:che@openerp.com</a>]
+                <br>
+                <b>Envoyé&nbsp;:</b> vendredi 4 octobre 2013 17:41<br>
+                <b>À&nbsp;:</b> <a href="mailto:infos@inzoservices.com">infos@inzoservices.com</a><br>
+                <b>Objet&nbsp;:</b> OpenERP S.A. Payment Reminder</span></p>
+            <p>&nbsp;</p>
+            <div>
+              <p style="background:white"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222">Dear
+
+                  INZO services,</span></p>
+              <p style="background:white"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222">Exception
+
+                  made if there was a mistake of ours, it seems that the
+                  following amount stays unpaid. Please, take
+                  appropriate measures in order to carry out this
+                  payment in the next 8 days. </span></p>
+              <p class="MsoNormal" style="background:white"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222"></span></p>
+              <p>&nbsp;</p>
+              <table class="MsoNormalTable" style="width:100.0%;border:outset 1.5pt" width="100%" border="1" cellpadding="0">
+                <tbody>
+                  <tr>
+                    <td style="padding:.75pt .75pt .75pt .75pt">
+                      <p class="MsoNormal">Date de facturation</p>
+                    </td>
+                    <td style="padding:.75pt .75pt .75pt .75pt">
+                      <p class="MsoNormal">Description</p>
+                    </td>
+                    <td style="padding:.75pt .75pt .75pt .75pt">
+                      <p class="MsoNormal">Reference</p>
+                    </td>
+                    <td style="padding:.75pt .75pt .75pt .75pt">
+                      <p class="MsoNormal">Due Date</p>
+                    </td>
+                    <td style="padding:.75pt .75pt .75pt .75pt">
+                      <p class="MsoNormal">Amount (€)</p>
+                    </td>
+                    <td style="padding:.75pt .75pt .75pt .75pt">
+                      <p class="MsoNormal">Lit.</p>
+                    </td>
+                  </tr>
+                  <tr>
+                    <td style="padding:.75pt .75pt .75pt .75pt">
+                      <p class="MsoNormal"><b>2013-09-24</b></p>
+                    </td>
+                    <td style="padding:.75pt .75pt .75pt .75pt">
+                      <p class="MsoNormal"><b>2013/1121</b></p>
+                    </td>
+                    <td style="padding:.75pt .75pt .75pt .75pt">
+                      <p class="MsoNormal"><b>Enterprise - Inzo Services
+                          - Juillet 2013</b></p>
+                    </td>
+                    <td style="padding:.75pt .75pt .75pt .75pt">
+                      <p class="MsoNormal"><b>2013-09-24</b></p>
+                    </td>
+                    <td style="padding:.75pt .75pt .75pt .75pt">
+                      <p class="MsoNormal"><b>420.0</b></p>
+                    </td>
+                    <td style="padding:.75pt .75pt .75pt .75pt"><br>
+                    </td>
+                  </tr>
+                  <tr>
+                    <td style="padding:.75pt .75pt .75pt .75pt"><br>
+                    </td>
+                    <td style="border:none;padding:.75pt .75pt .75pt
+                      .75pt"><br>
+                    </td>
+                    <td style="border:none;padding:.75pt .75pt .75pt
+                      .75pt"><br>
+                    </td>
+                    <td style="border:none;padding:.75pt .75pt .75pt
+                      .75pt"><br>
+                    </td>
+                    <td style="border:none;padding:.75pt .75pt .75pt
+                      .75pt"><br>
+                    </td>
+                    <td style="border:none;padding:.75pt .75pt .75pt
+                      .75pt"><br>
+                    </td>
+                  </tr>
+                </tbody>
+              </table>
+              <p class="MsoNormal" style="text-align:center;background:white" align="center"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222">Amount
+
+                  due : 420.00 € </span></p>
+              <p style="background:white"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222">Would
+
+                  your payment have been carried out after this mail was
+                  sent, please ignore this message. Do not hesitate to
+                  contact our accounting department. </span></p>
+              <p class="MsoNormal" style="background:white"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222"><br>
+                  Best Regards, <br>
+                  Aurore Lesage <br>
+                  OpenERP<br>
+                  Chaussée de Namur, 40 <br>
+                  B-1367 Grand Rosières <br>
+                  Tel: +32.81.81.37.00 - Fax: +32.81.73.35.01 <br>
+                  E-mail : <a href="mailto:ale@openerp.com">ale@openerp.com</a> <br>
+                  Web: <a href="http://www.openerp.com">http://www.openerp.com</a></span></p>
+            </div>
+          </div>
+        </div>
+        --<br>
+        INZO services <small>Sent by <a style="color:inherit" href="http://www.openerp.com">OpenERP
+            S.A.</a> using <a style="color:inherit" href="https://www.openerp.com/">OpenERP</a>.</small>
+        <small>Access your messages and documents <a style="color:inherit" href="https://accounts.openerp.com?db=openerp#action=mail.action_mail_redirect&amp;login=che&amp;message_id=5750830">in
+
+            OpenERP</a></small> <br>
+        <pre class="moz-signature" cols="72">-- 
+Christine Herrmann 
+
+OpenERP 
+Chaussée de Namur, 40 
+B-1367 Grand Rosières 
+Tel: +32.81.81.37.00 - Fax: +32.81.73.35.01 
+
+Web: <a class="moz-txt-link-freetext" href="http://www.openerp.com">http://www.openerp.com</a> </pre>
+        <br>
+      </div>
+      <br>
+      <br>
+    </div>
+    <br>
+  
+</div>"""
+
+BUG_2_IN = [
+    'read more',
+    '...',
+]
+BUG_2_OUT = [
+    'Fwd: TR: OpenERP S.A'
+    'fait un mois'
+]
diff --git a/openerp/addons/base/tests/test_misc.py b/openerp/addons/base/tests/test_misc.py
new file mode 100644 (file)
index 0000000..11cc3f4
--- /dev/null
@@ -0,0 +1,37 @@
+import unittest2
+
+from openerp.tools import misc
+
+
+class test_countingstream(unittest2.TestCase):
+    def test_empty_stream(self):
+        s = misc.CountingStream(iter([]))
+        self.assertEqual(s.index, -1)
+        self.assertIsNone(next(s, None))
+        self.assertEqual(s.index, 0)
+
+    def test_single(self):
+        s = misc.CountingStream(xrange(1))
+        self.assertEqual(s.index, -1)
+        self.assertEqual(next(s, None), 0)
+        self.assertIsNone(next(s, None))
+        self.assertEqual(s.index, 1)
+
+    def test_full(self):
+        s = misc.CountingStream(xrange(42))
+        for _ in s:
+            pass
+        self.assertEqual(s.index, 42)
+
+    def test_repeated(self):
+        """ Once the CountingStream has stopped iterating, the index should not
+        increase anymore (the internal state should not be allowed to change)
+        """
+        s = misc.CountingStream(iter([]))
+        self.assertIsNone(next(s, None))
+        self.assertEqual(s.index, 0)
+        self.assertIsNone(next(s, None))
+        self.assertEqual(s.index, 0)
+
+if __name__ == '__main__':
+    unittest2.main()
diff --git a/openerp/addons/base/tests/test_orm.py b/openerp/addons/base/tests/test_orm.py
new file mode 100644 (file)
index 0000000..fcd8196
--- /dev/null
@@ -0,0 +1,382 @@
+from collections import defaultdict
+from openerp.tools import mute_logger
+from openerp.tests import common
+
+UID = common.ADMIN_USER_ID
+DB = common.DB
+
+
+class TestORM(common.TransactionCase):
+    """ test special behaviors of ORM CRUD functions
+    
+        TODO: use real Exceptions types instead of Exception """
+
+    def setUp(self):
+        super(TestORM, self).setUp()
+        cr, uid = self.cr, self.uid
+        self.partner = self.registry('res.partner')
+        self.users = self.registry('res.users')
+        self.p1 = self.partner.name_create(cr, uid, 'W')[0]
+        self.p2 = self.partner.name_create(cr, uid, 'Y')[0]
+        self.ir_rule = self.registry('ir.rule')
+
+        # sample unprivileged user
+        employee_gid = self.ref('base.group_user')
+        self.uid2 = self.users.create(cr, uid, {'name': 'test user', 'login': 'test', 'groups_id': [4,employee_gid]})
+
+    @mute_logger('openerp.osv.orm')
+    def testAccessDeletedRecords(self):
+        """ Verify that accessing deleted records works as expected """
+        cr, uid, uid2, p1, p2 = self.cr, self.uid, self.uid2, self.p1, self.p2
+        self.partner.unlink(cr, uid, [p1])
+
+        # read() is expected to skip deleted records because our API is not
+        # transactional for a sequence of search()->read() performed from the
+        # client-side... a concurrent deletion could therefore cause spurious
+        # exceptions even when simply opening a list view!
+        # /!\ Using unprileged user to detect former side effects of ir.rules!
+        self.assertEqual([{'id': p2, 'name': 'Y'}], self.partner.read(cr, uid2, [p1,p2], ['name']), "read() should skip deleted records")
+        self.assertEqual([], self.partner.read(cr, uid2, [p1], ['name']), "read() should skip deleted records")
+
+        # Deleting an already deleted record should be simply ignored
+        self.assertTrue(self.partner.unlink(cr, uid, [p1]), "Re-deleting should be a no-op")
+
+        # Updating an already deleted record should raise, even as admin
+        with self.assertRaises(Exception):
+            self.partner.write(cr, uid, [p1], {'name': 'foo'})
+
+    @mute_logger('openerp.osv.orm')
+    def testAccessFilteredRecords(self):
+        """ Verify that accessing filtered records works as expected for non-admin user """
+        cr, uid, uid2, p1, p2 = self.cr, self.uid, self.uid2, self.p1, self.p2
+        partner_model = self.registry('ir.model').search(cr, uid, [('model','=','res.partner')])[0]
+        self.ir_rule.create(cr, uid, {'name': 'Y is invisible',
+                                      'domain_force': [('id', '!=', p1)],
+                                      'model_id': partner_model})
+        # search as unprivileged user
+        partners = self.partner.search(cr, uid2, [])
+        self.assertFalse(p1 in partners, "W should not be visible...")
+        self.assertTrue(p2 in partners, "... but Y should be visible")
+
+        # read as unprivileged user
+        with self.assertRaises(Exception):
+            self.partner.read(cr, uid2, [p1], ['name'])
+        # write as unprivileged user
+        with self.assertRaises(Exception):
+            self.partner.write(cr, uid2, [p1], {'name': 'foo'})
+        # unlink as unprivileged user
+        with self.assertRaises(Exception):
+            self.partner.unlink(cr, uid2, [p1])
+
+        # Prepare mixed case 
+        self.partner.unlink(cr, uid, [p2])
+        # read mixed records: some deleted and some filtered
+        with self.assertRaises(Exception):
+            self.partner.read(cr, uid2, [p1,p2], ['name'])
+        # delete mixed records: some deleted and some filtered
+        with self.assertRaises(Exception):
+            self.partner.unlink(cr, uid2, [p1,p2])
+
+    @mute_logger('openerp.osv.orm')
+    def test_search_read(self):
+        # simple search_read
+        self.partner.create(self.cr, UID, {'name': 'MyPartner1'})
+        found = self.partner.search_read(self.cr, UID, [['name', '=', 'MyPartner1']], ['name'])
+        self.assertEqual(len(found), 1)
+        self.assertEqual(found[0]['name'], 'MyPartner1')
+        self.assertTrue('id' in found[0])
+
+        # search_read correct order
+        self.partner.create(self.cr, UID, {'name': 'MyPartner2'})
+        found = self.partner.search_read(self.cr, UID, [['name', 'like', 'MyPartner']], ['name'], order="name")
+        self.assertEqual(len(found), 2)
+        self.assertEqual(found[0]['name'], 'MyPartner1')
+        self.assertEqual(found[1]['name'], 'MyPartner2')
+        found = self.partner.search_read(self.cr, UID, [['name', 'like', 'MyPartner']], ['name'], order="name desc")
+        self.assertEqual(len(found), 2)
+        self.assertEqual(found[0]['name'], 'MyPartner2')
+        self.assertEqual(found[1]['name'], 'MyPartner1')
+
+        # search_read that finds nothing
+        found = self.partner.search_read(self.cr, UID, [['name', '=', 'Does not exists']], ['name'])
+        self.assertEqual(len(found), 0)
+
+    def test_groupby_date(self):
+        partners = dict(
+            A='2012-11-19',
+            B='2012-12-17',
+            C='2012-12-31',
+            D='2013-01-07',
+            E='2013-01-14',
+            F='2013-01-28',
+            G='2013-02-11',
+        )
+
+        all_partners = []
+        partners_by_day = defaultdict(set)
+        partners_by_month = defaultdict(set)
+        partners_by_year = defaultdict(set)
+
+        for name, date in partners.items():
+            p = self.partner.create(self.cr, UID, dict(name=name, date=date))
+            all_partners.append(p)
+            partners_by_day[date].add(p)
+            partners_by_month[date.rsplit('-', 1)[0]].add(p)
+            partners_by_year[date.split('-', 1)[0]].add(p)
+
+        def read_group(interval, domain=None):
+            main_domain = [('id', 'in', all_partners)]
+            if domain:
+                domain = ['&'] + main_domain + domain
+            else:
+                domain = main_domain
+
+            rg = self.partner.read_group(self.cr, self.uid, domain, ['date'], 'date' + ':' + interval)
+            result = {}
+            for r in rg:
+                result[r['date']] = set(self.partner.search(self.cr, self.uid, r['__domain']))
+            return result
+
+        self.assertEqual(len(read_group('day')), len(partners_by_day))
+        self.assertEqual(len(read_group('month')), len(partners_by_month))
+        self.assertEqual(len(read_group('year')), len(partners_by_year))
+
+
+class TestInherits(common.TransactionCase):
+    """ test the behavior of the orm for models that use _inherits;
+        specifically: res.users, that inherits from res.partner
+    """
+
+    def setUp(self):
+        super(TestInherits, self).setUp()
+        self.partner = self.registry('res.partner')
+        self.user = self.registry('res.users')
+
+    def test_create(self):
+        """ creating a user should automatically create a new partner """
+        partners_before = self.partner.search(self.cr, UID, [])
+        foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
+        foo = self.user.browse(self.cr, UID, foo_id)
+
+        self.assertNotIn(foo.partner_id.id, partners_before)
+
+    def test_create_with_ancestor(self):
+        """ creating a user with a specific 'partner_id' should not create a new partner """
+        par_id = self.partner.create(self.cr, UID, {'name': 'Foo'})
+        partners_before = self.partner.search(self.cr, UID, [])
+        foo_id = self.user.create(self.cr, UID, {'partner_id': par_id, 'login': 'foo', 'password': 'foo'})
+        partners_after = self.partner.search(self.cr, UID, [])
+
+        self.assertEqual(set(partners_before), set(partners_after))
+
+        foo = self.user.browse(self.cr, UID, foo_id)
+        self.assertEqual(foo.name, 'Foo')
+        self.assertEqual(foo.partner_id.id, par_id)
+
+    @mute_logger('openerp.osv.orm')
+    def test_read(self):
+        """ inherited fields should be read without any indirection """
+        foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
+        foo_values, = self.user.read(self.cr, UID, [foo_id])
+        partner_id = foo_values['partner_id'][0]
+        partner_values, = self.partner.read(self.cr, UID, [partner_id])
+        self.assertEqual(foo_values['name'], partner_values['name'])
+
+        foo = self.user.browse(self.cr, UID, foo_id)
+        self.assertEqual(foo.name, foo.partner_id.name)
+
+    @mute_logger('openerp.osv.orm')
+    def test_copy(self):
+        """ copying a user should automatically copy its partner, too """
+        foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
+        foo_before, = self.user.read(self.cr, UID, [foo_id])
+        bar_id = self.user.copy(self.cr, UID, foo_id, {'login': 'bar', 'password': 'bar'})
+        foo_after, = self.user.read(self.cr, UID, [foo_id])
+
+        self.assertEqual(foo_before, foo_after)
+
+        foo, bar = self.user.browse(self.cr, UID, [foo_id, bar_id])
+        self.assertEqual(bar.login, 'bar')
+        self.assertNotEqual(foo.id, bar.id)
+        self.assertNotEqual(foo.partner_id.id, bar.partner_id.id)
+
+    @mute_logger('openerp.osv.orm')
+    def test_copy_with_ancestor(self):
+        """ copying a user with 'parent_id' in defaults should not duplicate the partner """
+        foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
+        par_id = self.partner.create(self.cr, UID, {'name': 'Bar'})
+
+        foo_before, = self.user.read(self.cr, UID, [foo_id])
+        partners_before = self.partner.search(self.cr, UID, [])
+        bar_id = self.user.copy(self.cr, UID, foo_id, {'partner_id': par_id, 'login': 'bar'})
+        foo_after, = self.user.read(self.cr, UID, [foo_id])
+        partners_after = self.partner.search(self.cr, UID, [])
+
+        self.assertEqual(foo_before, foo_after)
+        self.assertEqual(set(partners_before), set(partners_after))
+
+        foo, bar = self.user.browse(self.cr, UID, [foo_id, bar_id])
+        self.assertNotEqual(foo.id, bar.id)
+        self.assertEqual(bar.partner_id.id, par_id)
+        self.assertEqual(bar.login, 'bar', "login is given from copy parameters")
+        self.assertEqual(bar.password, foo.password, "password is given from original record")
+        self.assertEqual(bar.name, 'Bar', "name is given from specific partner")
+
+
+
+CREATE = lambda values: (0, False, values)
+UPDATE = lambda id, values: (1, id, values)
+DELETE = lambda id: (2, id, False)
+FORGET = lambda id: (3, id, False)
+LINK_TO = lambda id: (4, id, False)
+DELETE_ALL = lambda: (5, False, False)
+REPLACE_WITH = lambda ids: (6, False, ids)
+
+def sorted_by_id(list_of_dicts):
+    "sort dictionaries by their 'id' field; useful for comparisons"
+    return sorted(list_of_dicts, key=lambda d: d.get('id'))
+
+class TestO2MSerialization(common.TransactionCase):
+    """ test the orm method 'write' on one2many fields """
+
+    def setUp(self):
+        super(TestO2MSerialization, self).setUp()
+        self.partner = self.registry('res.partner')
+
+    def test_no_command(self):
+        " empty list of commands yields an empty list of records "
+        results = self.partner.resolve_2many_commands(
+            self.cr, UID, 'child_ids', [])
+
+        self.assertEqual(results, [])
+
+    def test_CREATE_commands(self):
+        " returns the VALUES dict as-is "
+        values = [{'foo': 'bar'}, {'foo': 'baz'}, {'foo': 'baq'}]
+        results = self.partner.resolve_2many_commands(
+            self.cr, UID, 'child_ids', map(CREATE, values))
+
+        self.assertEqual(results, values)
+
+    def test_LINK_TO_command(self):
+        " reads the records from the database, records are returned with their ids. "
+        ids = [
+            self.partner.create(self.cr, UID, {'name': 'foo'}),
+            self.partner.create(self.cr, UID, {'name': 'bar'}),
+            self.partner.create(self.cr, UID, {'name': 'baz'})
+        ]
+        commands = map(LINK_TO, ids)
+
+        results = self.partner.resolve_2many_commands(
+            self.cr, UID, 'child_ids', commands, ['name'])
+
+        self.assertEqual(sorted_by_id(results), sorted_by_id([
+            {'id': ids[0], 'name': 'foo'},
+            {'id': ids[1], 'name': 'bar'},
+            {'id': ids[2], 'name': 'baz'}
+        ]))
+
+    def test_bare_ids_command(self):
+        " same as the equivalent LINK_TO commands "
+        ids = [
+            self.partner.create(self.cr, UID, {'name': 'foo'}),
+            self.partner.create(self.cr, UID, {'name': 'bar'}),
+            self.partner.create(self.cr, UID, {'name': 'baz'})
+        ]
+
+        results = self.partner.resolve_2many_commands(
+            self.cr, UID, 'child_ids', ids, ['name'])
+
+        self.assertEqual(sorted_by_id(results), sorted_by_id([
+            {'id': ids[0], 'name': 'foo'},
+            {'id': ids[1], 'name': 'bar'},
+            {'id': ids[2], 'name': 'baz'}
+        ]))
+
+    def test_UPDATE_command(self):
+        " take the in-db records and merge the provided information in "
+        id_foo = self.partner.create(self.cr, UID, {'name': 'foo'})
+        id_bar = self.partner.create(self.cr, UID, {'name': 'bar'})
+        id_baz = self.partner.create(self.cr, UID, {'name': 'baz', 'city': 'tag'})
+
+        results = self.partner.resolve_2many_commands(
+            self.cr, UID, 'child_ids', [
+                LINK_TO(id_foo),
+                UPDATE(id_bar, {'name': 'qux', 'city': 'tagtag'}),
+                UPDATE(id_baz, {'name': 'quux'})
+            ], ['name', 'city'])
+
+        self.assertEqual(sorted_by_id(results), sorted_by_id([
+            {'id': id_foo, 'name': 'foo', 'city': False},
+            {'id': id_bar, 'name': 'qux', 'city': 'tagtag'},
+            {'id': id_baz, 'name': 'quux', 'city': 'tag'}
+        ]))
+
+    def test_DELETE_command(self):
+        " deleted records are not returned at all. "
+        ids = [
+            self.partner.create(self.cr, UID, {'name': 'foo'}),
+            self.partner.create(self.cr, UID, {'name': 'bar'}),
+            self.partner.create(self.cr, UID, {'name': 'baz'})
+        ]
+        commands = [DELETE(ids[0]), DELETE(ids[1]), DELETE(ids[2])]
+
+        results = self.partner.resolve_2many_commands(
+            self.cr, UID, 'child_ids', commands, ['name'])
+
+        self.assertEqual(results, [])
+
+    def test_mixed_commands(self):
+        ids = [
+            self.partner.create(self.cr, UID, {'name': name})
+            for name in ['NObar', 'baz', 'qux', 'NOquux', 'NOcorge', 'garply']
+        ]
+
+        results = self.partner.resolve_2many_commands(
+            self.cr, UID, 'child_ids', [
+                CREATE({'name': 'foo'}),
+                UPDATE(ids[0], {'name': 'bar'}),
+                LINK_TO(ids[1]),
+                DELETE(ids[2]),
+                UPDATE(ids[3], {'name': 'quux',}),
+                UPDATE(ids[4], {'name': 'corge'}),
+                CREATE({'name': 'grault'}),
+                LINK_TO(ids[5])
+            ], ['name'])
+
+        self.assertEqual(sorted_by_id(results), sorted_by_id([
+            {'name': 'foo'},
+            {'id': ids[0], 'name': 'bar'},
+            {'id': ids[1], 'name': 'baz'},
+            {'id': ids[3], 'name': 'quux'},
+            {'id': ids[4], 'name': 'corge'},
+            {'name': 'grault'},
+            {'id': ids[5], 'name': 'garply'}
+        ]))
+
+    def test_LINK_TO_pairs(self):
+        "LINK_TO commands can be written as pairs, instead of triplets"
+        ids = [
+            self.partner.create(self.cr, UID, {'name': 'foo'}),
+            self.partner.create(self.cr, UID, {'name': 'bar'}),
+            self.partner.create(self.cr, UID, {'name': 'baz'})
+        ]
+        commands = map(lambda id: (4, id), ids)
+
+        results = self.partner.resolve_2many_commands(
+            self.cr, UID, 'child_ids', commands, ['name'])
+
+        self.assertEqual(sorted_by_id(results), sorted_by_id([
+            {'id': ids[0], 'name': 'foo'},
+            {'id': ids[1], 'name': 'bar'},
+            {'id': ids[2], 'name': 'baz'}
+        ]))
+
+    def test_singleton_commands(self):
+        "DELETE_ALL can appear as a singleton"
+        results = self.partner.resolve_2many_commands(
+            self.cr, UID, 'child_ids', [DELETE_ALL()], ['name'])
+
+        self.assertEqual(results, [])
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/tests/test_osv.py b/openerp/addons/base/tests/test_osv.py
new file mode 100644 (file)
index 0000000..e36495f
--- /dev/null
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2010 OpenERP S.A. http://www.openerp.com
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import unittest
+from openerp.osv.query import Query
+
+
+class QueryTestCase(unittest.TestCase):
+
+    def test_basic_query(self):
+        query = Query()
+        query.tables.extend(['"product_product"', '"product_template"'])
+        query.where_clause.append("product_product.template_id = product_template.id")
+        query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False)  # add normal join
+        query.add_join(("product_product", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True)  # outer join
+        self.assertEquals(query.get_sql()[0].strip(),
+            """"product_product" LEFT JOIN "res_user" as "product_product__user_id" ON ("product_product"."user_id" = "product_product__user_id"."id"),"product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") """.strip())
+        self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip())
+
+    def test_query_chained_explicit_joins(self):
+        query = Query()
+        query.tables.extend(['"product_product"', '"product_template"'])
+        query.where_clause.append("product_product.template_id = product_template.id")
+        query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False)  # add normal join
+        query.add_join(("product_template__categ_id", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True)  # CHAINED outer join
+        self.assertEquals(query.get_sql()[0].strip(),
+            """"product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id")""".strip())
+        self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip())
+
+    def test_mixed_query_chained_explicit_implicit_joins(self):
+        query = Query()
+        query.tables.extend(['"product_product"', '"product_template"'])
+        query.where_clause.append("product_product.template_id = product_template.id")
+        query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False)  # add normal join
+        query.add_join(("product_template__categ_id", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True)  # CHAINED outer join
+        query.tables.append('"account.account"')
+        query.where_clause.append("product_category.expense_account_id = account_account.id")  # additional implicit join
+        self.assertEquals(query.get_sql()[0].strip(),
+            """"product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id"),"account.account" """.strip())
+        self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id AND product_category.expense_account_id = account_account.id""".strip())
+
+    def test_raise_missing_lhs(self):
+        query = Query()
+        query.tables.append('"product_product"')
+        self.assertRaises(AssertionError, query.add_join, ("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False)
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/tests/test_phantom.py b/openerp/addons/base/tests/test_phantom.py
new file mode 100644 (file)
index 0000000..e729b2e
--- /dev/null
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+
+import openerp
+from openerp.tests import common
+
+class test_phantom(common.HttpCase):
+
+    def test_01_dummy(self):
+        self.phantomjs(openerp.modules.module.get_module_resource('base','tests','test_phantom_dummy.js'))
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/tests/test_phantom_dummy.js b/openerp/addons/base/tests/test_phantom_dummy.js
new file mode 100644 (file)
index 0000000..7a1f2b3
--- /dev/null
@@ -0,0 +1,4 @@
+console.log('{ "event": "success", "message": "Phantomjs success"}');
+// For a failure:
+// console.log('{ "event": "failure", "message": "The test failed" }');
+phantom.exit();
diff --git a/openerp/addons/base/tests/test_qweb.py b/openerp/addons/base/tests/test_qweb.py
new file mode 100644 (file)
index 0000000..48e8dc3
--- /dev/null
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+import cgi
+from xml.dom import minidom as dom
+
+import common
+from openerp.addons.base.ir import ir_qweb
+
+impl = dom.getDOMImplementation()
+document = impl.createDocument(None, None, None)
+
+class TestQWebTField(common.TransactionCase):
+    def setUp(self):
+        super(TestQWebTField, self).setUp()
+        self.engine = self.registry('ir.qweb')
+
+    def context(self, values):
+        return ir_qweb.QWebContext(self.cr, self.uid, values)
+
+    def test_trivial(self):
+        field = document.createElement('span')
+        field.setAttribute('t-field', u'company.name')
+
+        Companies = self.registry('res.company')
+        company_id = Companies.create(self.cr, self.uid, {
+            'name': "My Test Company"
+        })
+        result = self.engine.render_node(field, self.context({
+            'company': Companies.browse(self.cr, self.uid, company_id),
+        }))
+
+        self.assertEqual(
+            result,
+            '<span data-oe-model="res.company" data-oe-id="%d" '
+                  'data-oe-field="name" data-oe-type="char" '
+                  'data-oe-expression="company.name">%s</span>' % (
+                company_id,
+                "My Test Company",))
+
+    def test_i18n(self):
+        field = document.createElement('span')
+        field.setAttribute('t-field', u'company.name')
+
+        Companies = self.registry('res.company')
+        s = u"Testing «ταБЬℓσ»: 1<2 & 4+1>3, now 20% off!"
+        company_id = Companies.create(self.cr, self.uid, {
+            'name': s,
+        })
+        result = self.engine.render_node(field, self.context({
+            'company': Companies.browse(self.cr, self.uid, company_id),
+        }))
+
+        self.assertEqual(
+            result,
+            '<span data-oe-model="res.company" data-oe-id="%d" '
+                  'data-oe-field="name" data-oe-type="char" '
+                  'data-oe-expression="company.name">%s</span>' % (
+                company_id,
+                cgi.escape(s.encode('utf-8')),))
+
+    def test_reject_crummy_tags(self):
+        field = document.createElement('td')
+        field.setAttribute('t-field', u'company.name')
+
+        with self.assertRaisesRegexp(
+                AssertionError,
+                r'^RTE widgets do not work correctly'):
+            self.engine.render_node(field, self.context({
+                'company': None
+            }))
+
+    def test_reject_t_tag(self):
+        field = document.createElement('t')
+        field.setAttribute('t-field', u'company.name')
+
+        with self.assertRaisesRegexp(
+                AssertionError,
+                r'^t-field can not be used on a t element'):
+            self.engine.render_node(field, self.context({
+                'company': None
+            }))
diff --git a/openerp/addons/base/tests/test_translate.py b/openerp/addons/base/tests/test_translate.py
new file mode 100644 (file)
index 0000000..d9f771b
--- /dev/null
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2010 OpenERP S.A. http://www.openerp.com
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import unittest
+from openerp.tools.translate import quote, unquote
+
+class TranslationToolsTestCase(unittest.TestCase):
+
+    def test_quote_unquote(self):
+
+        def test_string(str):
+            quoted = quote(str)
+            #print "\n1:", repr(str)
+            #print "2:", repr(quoted)
+            unquoted = unquote("".join(quoted.split('"\n"')))
+            #print "3:", repr(unquoted)
+            self.assertEquals(str, unquoted)
+
+        test_string("""test \nall kinds\n \n o\r
+         \\\\ nope\n\n"
+         """)
+
+        # The ones with 1+ backslashes directly followed by
+        # a newline or literal N can fail... we would need a
+        # state-machine parser to handle these, but this would
+        # be much slower so it's better to avoid them at the moment
+        self.assertRaises(AssertionError, quote, """test \nall kinds\n\no\r
+         \\\\nope\n\n"
+         """)
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/tests/test_uninstall.py b/openerp/addons/base/tests/test_uninstall.py
new file mode 100644 (file)
index 0000000..2425249
--- /dev/null
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+# This assumes an existing but uninitialized database.
+import unittest2
+
+import openerp
+from openerp import SUPERUSER_ID
+import common
+
+DB = common.DB
+ADMIN_USER_ID = common.ADMIN_USER_ID
+
+def registry(model):
+    return openerp.modules.registry.RegistryManager.get(DB)[model]
+
+def cursor():
+    return openerp.modules.registry.RegistryManager.get(DB).db.cursor()
+
+def get_module(module_name):
+    registry = openerp.modules.registry.RegistryManager.get(DB)
+    return registry.get(module_name)
+
+def reload_registry():
+    openerp.modules.registry.RegistryManager.new(
+        DB, update_module=True)
+
+def search_registry(model_name, domain):
+    cr = cursor()
+    model = registry(model_name)
+    record_ids = model.search(cr, SUPERUSER_ID, domain, {})
+    cr.close()
+    return record_ids
+
+def install_module(module_name):
+    ir_module_module = registry('ir.module.module')
+    cr = cursor()
+    module_ids = ir_module_module.search(cr, SUPERUSER_ID,
+        [('name', '=', module_name)], {})
+    assert len(module_ids) == 1
+    ir_module_module.button_install(cr, SUPERUSER_ID, module_ids, {})
+    cr.commit()
+    cr.close()
+    reload_registry()
+
+def uninstall_module(module_name):
+    ir_module_module = registry('ir.module.module')
+    cr = cursor()
+    module_ids = ir_module_module.search(cr, SUPERUSER_ID,
+        [('name', '=', module_name)], {})
+    assert len(module_ids) == 1
+    ir_module_module.button_uninstall(cr, SUPERUSER_ID, module_ids, {})
+    cr.commit()
+    cr.close()
+    reload_registry()
+
+class test_uninstall(unittest2.TestCase):
+    """
+    Test the install/uninstall of a test module. The module is available in
+    `openerp.tests` which should be present in the addons-path.
+    """
+
+    def test_01_install(self):
+        """ Check a few things showing the module is installed. """
+        install_module('test_uninstall')
+        assert get_module('test_uninstall.model')
+
+        assert search_registry('ir.model.data',
+            [('module', '=', 'test_uninstall')])
+
+        assert search_registry('ir.model.fields',
+            [('model', '=', 'test_uninstall.model')])
+
+    def test_02_uninstall(self):
+        """ Check a few things showing the module is uninstalled. """
+        uninstall_module('test_uninstall')
+        assert not get_module('test_uninstall.model')
+
+        assert not search_registry('ir.model.data',
+            [('module', '=', 'test_uninstall')])
+
+        assert not search_registry('ir.model.fields',
+            [('model', '=', 'test_uninstall.model')])
+
+
+
+if __name__ == '__main__':
+    unittest2.main()
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/tests/test_view_validation.py b/openerp/addons/base/tests/test_view_validation.py
new file mode 100644 (file)
index 0000000..4cd8b95
--- /dev/null
@@ -0,0 +1,131 @@
+# This test can be run stand-alone with something like:
+# > PYTHONPATH=. python2 openerp/tests/test_view_validation.py
+from lxml import etree
+from StringIO import StringIO
+import unittest2
+
+from openerp.tools.view_validation import (valid_page_in_book, valid_att_in_form, valid_type_in_colspan,
+                                           valid_type_in_col, valid_att_in_field, valid_att_in_label,
+                                           valid_field_in_graph, valid_field_in_tree
+                                           )
+
+invalid_form = etree.parse(StringIO('''\
+<form>
+    <label></label>
+    <group>
+        <div>
+            <page></page>
+            <label colspan="True"></label>
+            <field></field>
+        </div>
+    </group>
+    <notebook>
+        <page>
+            <group col="Two">
+            <div>
+                <label></label>
+                <field colspan="Five"> </field>
+                </div>
+            </group>
+        </page>
+    </notebook>
+</form>
+''')).getroot()
+
+valid_form = etree.parse(StringIO('''\
+<form string="">
+    <field name=""></field>
+    <field name=""></field>
+    <notebook>
+        <page>
+            <field name=""></field>
+            <label string=""></label>
+            <field name=""></field>
+        </page>
+        <page>
+            <group colspan="5" col="2">
+                <label for=""></label>
+                <label string="" colspan="5"></label>
+            </group>
+        </page>
+    </notebook>
+</form>
+''')).getroot()
+
+invalid_graph = etree.parse(StringIO('''\
+<graph>
+    <label/>
+    <group>
+        <div>
+            <field></field>
+            <field></field>
+        </div>
+    </group>
+</graph>
+''')).getroot()
+
+valid_graph = etree.parse(StringIO('''\
+<graph string="">
+    <field name=""></field>
+    <field name=""></field>
+</graph>
+''')).getroot()
+
+invalid_tree = etree.parse(StringIO('''\
+<tree>
+  <group>
+    <div>
+      <field></field>
+      <field></field>
+    </div>
+  </group>
+</tree>
+''')).getroot()
+
+valid_tree = etree.parse(StringIO('''\
+<tree string="">
+    <field name=""></field>
+    <field name=""></field>
+    <button/>
+    <field name=""></field>
+</tree>
+''')).getroot()
+
+
+class test_view_validation(unittest2.TestCase):
+    """ Test the view validation code (but not the views themselves). """
+
+    def test_page_validation(self):
+        assert not valid_page_in_book(invalid_form)
+        assert valid_page_in_book(valid_form)
+
+    def test_all_field_validation(self):
+        assert not valid_att_in_field(invalid_form)
+        assert valid_att_in_field(valid_form)
+
+    def test_all_label_validation(self):
+        assert not valid_att_in_label(invalid_form)
+        assert valid_att_in_label(valid_form)
+
+    def test_form_string_validation(self):
+        assert valid_att_in_form(valid_form)
+
+    def test_graph_validation(self):
+        assert not valid_field_in_graph(invalid_graph)
+        assert valid_field_in_graph(valid_graph)
+
+    def test_tree_validation(self):
+        assert not valid_field_in_tree(invalid_tree)
+        assert valid_field_in_tree(valid_tree)
+
+    def test_colspan_datatype_validation(self):
+        assert not valid_type_in_colspan(invalid_form)
+        assert valid_type_in_colspan(valid_form)
+
+    def test_col_datatype_validation(self):
+        assert not valid_type_in_col(invalid_form)
+        assert valid_type_in_col(valid_form)
+
+
+if __name__ == '__main__':
+    unittest2.main()
diff --git a/openerp/addons/base/tests/test_xmlrpc.py b/openerp/addons/base/tests/test_xmlrpc.py
new file mode 100644 (file)
index 0000000..00be7e7
--- /dev/null
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+import time
+import unittest2
+import xmlrpclib
+
+from openerp.tests import common
+
+DB = common.DB
+ADMIN_USER = common.ADMIN_USER
+ADMIN_USER_ID = common.ADMIN_USER_ID
+ADMIN_PASSWORD = common.ADMIN_PASSWORD
+
+class test_xmlrpc(common.HttpCase):
+
+    def test_01_xmlrpc_login(self):
+        """ Try to login on the common service. """
+        uid = self.xmlrpc_common.login(DB, ADMIN_USER, ADMIN_PASSWORD)
+        self.assertTrue(uid == ADMIN_USER_ID)
+
+    def test_xmlrpc_ir_model_search(self):
+        """ Try a search on the object service. """
+        o = self.xmlrpc_object
+        ids = o.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, 'ir.model', 'search', [])
+        self.assertIsInstance(ids, list)
+        ids = o.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, 'ir.model', 'search', [], {})
+        self.assertIsInstance(ids, list)
+
+    # This test was written to test the creation of a new RPC endpoint, not
+    # really for the EDI itself.
+    #def test_xmlrpc_import_edi_document(self):
+    #    """ Try to call an EDI method. """
+    #    msg_re = 'EDI Document is empty!'
+    #    with self.assertRaisesRegexp(Exception, msg_re):
+    #        self.proxy.edi_60.import_edi_document(DB, ADMIN_USER_ID, ADMIN_PASSWORD, {})
+
+if __name__ == '__main__':
+    unittest2.main()
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_convert/__init__.py b/openerp/addons/test_convert/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/openerp/addons/test_convert/__openerp__.py b/openerp/addons/test_convert/__openerp__.py
new file mode 100644 (file)
index 0000000..40d9eb3
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    'name': 'test_convert',
+    'description': "Data for xml conversion tests",
+    'version': '0.0.1',
+}
diff --git a/openerp/addons/test_convert/test_file.txt b/openerp/addons/test_convert/test_file.txt
new file mode 100644 (file)
index 0000000..e69b2e0
--- /dev/null
@@ -0,0 +1 @@
+nothing to see here, move along
diff --git a/openerp/addons/test_convert/tests/__init__.py b/openerp/addons/test_convert/tests/__init__.py
new file mode 100644 (file)
index 0000000..002a0a1
--- /dev/null
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+from . import test_convert
+
+checks = [
+    test_convert
+]
diff --git a/openerp/addons/test_convert/tests/test_convert.py b/openerp/addons/test_convert/tests/test_convert.py
new file mode 100644 (file)
index 0000000..50d7df4
--- /dev/null
@@ -0,0 +1,83 @@
+import collections
+import unittest2
+from lxml import etree as ET
+from lxml.builder import E
+
+from openerp.tests import common
+
+from openerp.tools.convert import _eval_xml
+
+Field = E.field
+Value = E.value
+class TestEvalXML(common.TransactionCase):
+    def eval_xml(self, node, obj=None, idref=None):
+        return _eval_xml(obj, node, pool=None, cr=self.cr, uid=self.uid,
+                         idref=idref, context=None)
+
+    def test_char(self):
+        self.assertEqual(
+            self.eval_xml(Field("foo")),
+            "foo")
+        self.assertEqual(
+            self.eval_xml(Field("None")),
+            "None")
+
+    def test_int(self):
+        self.assertIsNone(
+            self.eval_xml(Field("None", type='int')),
+            "what the fuck?")
+        self.assertEqual(
+            self.eval_xml(Field(" 42  ", type="int")),
+            42)
+
+        with self.assertRaises(ValueError):
+            self.eval_xml(Field("4.82", type="int"))
+
+        with self.assertRaises(ValueError):
+            self.eval_xml(Field("Whelp", type="int"))
+
+    def test_float(self):
+        self.assertEqual(
+            self.eval_xml(Field("4.78", type="float")),
+            4.78)
+
+        with self.assertRaises(ValueError):
+            self.eval_xml(Field("None", type="float"))
+
+        with self.assertRaises(ValueError):
+            self.eval_xml(Field("Foo", type="float"))
+
+    def test_list(self):
+        self.assertEqual(
+            self.eval_xml(Field(type="list")),
+            [])
+
+        self.assertEqual(
+            self.eval_xml(Field(
+                Value("foo"),
+                Value("5", type="int"),
+                Value("4.76", type="float"),
+                Value("None", type="int"),
+                type="list"
+            )),
+            ["foo", 5, 4.76, None])
+
+    def test_file(self):
+        Obj = collections.namedtuple('Obj', 'module')
+        obj = Obj('test_convert')
+        self.assertEqual(
+            self.eval_xml(Field('test_file.txt', type='file'), obj),
+            'test_convert,test_file.txt')
+
+        with self.assertRaises(IOError):
+            self.eval_xml(Field('test_nofile.txt', type='file'), obj)
+
+    @unittest2.skip("not tested")
+    def test_xml(self):
+        pass
+
+    @unittest2.skip("not tested")
+    def test_html(self):
+        pass
+
+
diff --git a/openerp/addons/test_converter/__init__.py b/openerp/addons/test_converter/__init__.py
new file mode 100644 (file)
index 0000000..89d26e2
--- /dev/null
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+import models
diff --git a/openerp/addons/test_converter/__openerp__.py b/openerp/addons/test_converter/__openerp__.py
new file mode 100644 (file)
index 0000000..c76b1fe
--- /dev/null
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+{
+    'name': 'test-field-converter',
+    'version': '0.1',
+    'category': 'Tests',
+    'description': """Tests of field conversions""",
+    'author': 'OpenERP SA',
+    'maintainer': 'OpenERP SA',
+    'website': 'http://www.openerp.com',
+    'depends': ['base'],
+    'data': ['ir.model.access.csv'],
+    'installable': True,
+    'auto_install': False,
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_converter/ir.model.access.csv b/openerp/addons/test_converter/ir.model.access.csv
new file mode 100644 (file)
index 0000000..ff4ac97
--- /dev/null
@@ -0,0 +1,4 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_converter_model,access_converter_model,model_test_converter_test_model,,1,1,1,1
+access_test_converter_test_model_sub,access_test_converter_test_model_sub,model_test_converter_test_model_sub,,1,1,1,1
+access_test_converter_monetary,access_test_converter_monetary,model_test_converter_monetary,,1,1,1,1
diff --git a/openerp/addons/test_converter/models.py b/openerp/addons/test_converter/models.py
new file mode 100644 (file)
index 0000000..9074ba8
--- /dev/null
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+from openerp.osv import orm, fields
+
+class test_model(orm.Model):
+    _name = 'test_converter.test_model'
+
+    _columns = {
+        'char': fields.char(),
+        'integer': fields.integer(),
+        'float': fields.float(),
+        'numeric': fields.float(digits=(16, 2)),
+        'many2one': fields.many2one('test_converter.test_model.sub'),
+        'binary': fields.binary(),
+        'date': fields.date(),
+        'datetime': fields.datetime(),
+        'selection': fields.selection([
+            (1, "réponse A"),
+            (2, "réponse B"),
+            (3, "réponse C"),
+            (4, "réponse D"),
+        ]),
+        'selection_str': fields.selection([
+            ('A', "Qu'il n'est pas arrivé à Toronto"),
+            ('B', "Qu'il était supposé arriver à Toronto"),
+            ('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
+            ('D', "La réponse D"),
+        ], string="Lorsqu'un pancake prend l'avion à destination de Toronto et "
+                  "qu'il fait une escale technique à St Claude, on dit:"),
+        'html': fields.html(),
+        'text': fields.text(),
+    }
+
+class test_model_sub(orm.Model):
+    _name = 'test_converter.test_model.sub'
+
+    _columns = {
+        'name': fields.char()
+    }
+
+
+class test_model_monetary(orm.Model):
+    _name = 'test_converter.monetary'
+
+    _columns = {
+        'value': fields.float(digits=(16, 55)),
+    }
diff --git a/openerp/addons/test_converter/tests/__init__.py b/openerp/addons/test_converter/tests/__init__.py
new file mode 100644 (file)
index 0000000..493b164
--- /dev/null
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+
+from . import test_html
+
+fast_suite = [
+]
+
+checks = [
+    test_html
+]
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_converter/tests/test_html.py b/openerp/addons/test_converter/tests/test_html.py
new file mode 100644 (file)
index 0000000..2f08969
--- /dev/null
@@ -0,0 +1,387 @@
+# -*- encoding: utf-8 -*-
+import json
+import os
+import xml.dom.minidom
+import datetime
+
+from werkzeug.utils import escape as e
+
+from openerp.tests import common
+from openerp.addons.base.ir import ir_qweb
+
+directory = os.path.dirname(__file__)
+
+impl = xml.dom.minidom.getDOMImplementation()
+doc = impl.createDocument(None, None, None)
+
+class TestExport(common.TransactionCase):
+    _model = None
+
+    def setUp(self):
+        super(TestExport, self).setUp()
+        self.Model = self.registry(self._model)
+        self.columns = self.Model._all_columns
+
+    def get_column(self, name):
+        return self.Model._all_columns[name].column
+
+    def get_converter(self, name, type=None):
+        column = self.get_column(name)
+
+        for postfix in type, column._type, '':
+            fs = ['ir', 'qweb', 'field']
+            if postfix is None: continue
+            if postfix: fs.append(postfix)
+
+            try:
+                model = self.registry('.'.join(fs))
+                break
+            except KeyError: pass
+
+        return lambda value, options=None, context=None: e(model.value_to_html(
+            self.cr, self.uid, value, column, options=options, context=context))
+
+class TestBasicExport(TestExport):
+    _model = 'test_converter.test_model'
+
+class TestCharExport(TestBasicExport):
+    def test_char(self):
+        converter = self.get_converter('char')
+
+        value = converter('foo')
+        self.assertEqual(value, 'foo')
+
+        value = converter("foo<bar>")
+        self.assertEqual(value, "foo&lt;bar&gt;")
+
+class TestIntegerExport(TestBasicExport):
+    def test_integer(self):
+        converter = self.get_converter('integer')
+
+        value = converter(42)
+        self.assertEqual(value, "42")
+
+class TestFloatExport(TestBasicExport):
+    def setUp(self):
+        super(TestFloatExport, self).setUp()
+        self.registry('res.lang').write(self.cr, self.uid, [1], {
+            'grouping': '[3,0]'
+        })
+
+    def test_float(self):
+        converter = self.get_converter('float')
+
+        value = converter(42.0)
+        self.assertEqual(value, "42.0")
+
+        value = converter(42.0100)
+        self.assertEqual(value, "42.01")
+
+        value = converter(42.01234)
+        self.assertEqual(value, "42.01234")
+
+        value = converter(1234567.89)
+        self.assertEqual(value, '1,234,567.89')
+
+    def test_numeric(self):
+        converter = self.get_converter('numeric')
+
+        value = converter(42.0)
+        self.assertEqual(value, '42.00')
+
+        value = converter(42.01234)
+        self.assertEqual(value, '42.01')
+
+class TestCurrencyExport(TestExport):
+    _model = 'test_converter.monetary'
+
+    def setUp(self):
+        super(TestCurrencyExport, self).setUp()
+        self.Currency = self.registry('res.currency')
+        self.base = self.create(self.Currency, name="Source", symbol=u'source')
+
+    def create(self, model, context=None, **values):
+        return model.browse(
+            self.cr, self.uid,
+            model.create(self.cr, self.uid, values, context=context),
+            context=context)
+
+    def convert(self, obj, dest):
+        converter = self.registry('ir.qweb.field.monetary')
+        options = {
+            'widget': 'monetary',
+            'display_currency': 'c2'
+        }
+        converted = converter.to_html(
+            self.cr, self.uid, 'value', obj, options,
+            doc.createElement('span'),
+            {'field': 'obj.value', 'field-options': json.dumps(options)},
+            '', ir_qweb.QWebContext(self.cr, self.uid, {'obj': obj, 'c2': dest, }))
+        return converted
+
+    def test_currency_post(self):
+        currency = self.create(self.Currency, name="Test", symbol=u"test")
+        obj = self.create(self.Model, value=0.12)
+
+        converted = self.convert(obj, dest=currency)
+
+        self.assertEqual(
+            converted,
+            '<span data-oe-model="{obj._model._name}" data-oe-id="{obj.id}" '
+                  'data-oe-field="value" data-oe-type="monetary" '
+                  'data-oe-expression="obj.value">'
+                      '<span class="oe_currency_value">0.12</span>'
+                      ' {symbol}</span>'.format(
+                obj=obj,
+                symbol=currency.symbol.encode('utf-8')
+            ),)
+
+    def test_currency_pre(self):
+        currency = self.create(
+            self.Currency, name="Test", symbol=u"test", position='before')
+        obj = self.create(self.Model, value=0.12)
+
+        converted = self.convert(obj, dest=currency)
+
+        self.assertEqual(
+            converted,
+            '<span data-oe-model="{obj._model._name}" data-oe-id="{obj.id}" '
+                  'data-oe-field="value" data-oe-type="monetary" '
+                  'data-oe-expression="obj.value">'
+                      '{symbol} '
+                      '<span class="oe_currency_value">0.12</span>'
+                      '</span>'.format(
+                obj=obj,
+                symbol=currency.symbol.encode('utf-8')
+            ),)
+
+    def test_currency_precision(self):
+        """ Precision should be the currency's, not the float field's
+        """
+        currency = self.create(self.Currency, name="Test", symbol=u"test",)
+        obj = self.create(self.Model, value=0.1234567)
+
+        converted = self.convert(obj, dest=currency)
+
+        self.assertEqual(
+            converted,
+            '<span data-oe-model="{obj._model._name}" data-oe-id="{obj.id}" '
+                  'data-oe-field="value" data-oe-type="monetary" '
+                  'data-oe-expression="obj.value">'
+                      '<span class="oe_currency_value">0.12</span>'
+                      ' {symbol}</span>'.format(
+                obj=obj,
+                symbol=currency.symbol.encode('utf-8')
+            ),)
+
+class TestTextExport(TestBasicExport):
+    def test_text(self):
+        converter = self.get_converter('text')
+
+        value = converter("This is my text-kai")
+        self.assertEqual(value, "This is my text-kai")
+
+        value = converter("""
+            .  The current line (address) in the buffer.
+            $  The last line in the buffer.
+            n  The nth, line in the buffer where n is a number in the range [0,$].
+            $  The last line in the buffer.
+            -  The previous line. This is equivalent to -1 and may be repeated with cumulative effect.
+            -n The nth previous line, where n is a non-negative number.
+            +  The next line. This is equivalent to +1 and may be repeated with cumulative effect.
+        """)
+        self.assertEqual(value, """<br>
+            .  The current line (address) in the buffer.<br>
+            $  The last line in the buffer.<br>
+            n  The nth, line in the buffer where n is a number in the range [0,$].<br>
+            $  The last line in the buffer.<br>
+            -  The previous line. This is equivalent to -1 and may be repeated with cumulative effect.<br>
+            -n The nth previous line, where n is a non-negative number.<br>
+            +  The next line. This is equivalent to +1 and may be repeated with cumulative effect.<br>
+        """)
+
+        value = converter("""
+        fgdkls;hjas;lj <b>fdslkj</b> d;lasjfa lkdja <a href=http://spam.com>lfks</a>
+        fldkjsfhs <i style="color: red"><a href="http://spamspam.com">fldskjh</a></i>
+        """)
+        self.assertEqual(value, """<br>
+        fgdkls;hjas;lj &lt;b&gt;fdslkj&lt;/b&gt; d;lasjfa lkdja &lt;a href=http://spam.com&gt;lfks&lt;/a&gt;<br>
+        fldkjsfhs &lt;i style=&quot;color: red&quot;&gt;&lt;a href=&quot;http://spamspam.com&quot;&gt;fldskjh&lt;/a&gt;&lt;/i&gt;<br>
+        """)
+
+class TestMany2OneExport(TestBasicExport):
+    def test_many2one(self):
+        Sub = self.registry('test_converter.test_model.sub')
+
+
+        id0 = self.Model.create(self.cr, self.uid, {
+            'many2one': Sub.create(self.cr, self.uid, {'name': "Foo"})
+        })
+        id1 = self.Model.create(self.cr, self.uid, {
+            'many2one': Sub.create(self.cr, self.uid, {'name': "Fo<b>o</b>"})
+        })
+
+        def converter(record):
+            column = self.get_column('many2one')
+            model = self.registry('ir.qweb.field.many2one')
+
+            return e(model.record_to_html(
+                self.cr, self.uid, 'many2one', record, column))
+
+        value = converter(self.Model.browse(self.cr, self.uid, id0))
+        self.assertEqual(value, "Foo")
+
+        value = converter(self.Model.browse(self.cr, self.uid, id1))
+        self.assertEqual(value, "Fo&lt;b&gt;o&lt;/b&gt;")
+
+class TestBinaryExport(TestBasicExport):
+    def test_image(self):
+        column = self.get_column('binary')
+        converter = self.registry('ir.qweb.field.image')
+
+        with open(os.path.join(directory, 'test_vectors', 'image'), 'rb') as f:
+            content = f.read()
+
+        encoded_content = content.encode('base64')
+        value = e(converter.value_to_html(
+            self.cr, self.uid, encoded_content, column))
+        self.assertEqual(
+            value, '<img src="data:image/jpeg;base64,%s">' % (
+                encoded_content
+            ))
+
+        with open(os.path.join(directory, 'test_vectors', 'pdf'), 'rb') as f:
+            content = f.read()
+
+        with self.assertRaises(ValueError):
+            e(converter.value_to_html(
+                self.cr, self.uid, 'binary', content.encode('base64'), column))
+
+        with open(os.path.join(directory, 'test_vectors', 'pptx'), 'rb') as f:
+            content = f.read()
+
+        with self.assertRaises(ValueError):
+            e(converter.value_to_html(
+                self.cr, self.uid, 'binary', content.encode('base64'), column))
+
+class TestSelectionExport(TestBasicExport):
+    def test_selection(self):
+        [record] = self.Model.browse(self.cr, self.uid, [self.Model.create(self.cr, self.uid, {
+            'selection': 2,
+            'selection_str': 'C',
+        })])
+
+        column_name = 'selection'
+        column = self.get_column(column_name)
+        converter = self.registry('ir.qweb.field.selection')
+
+        value = converter.record_to_html(
+            self.cr, self.uid, column_name, record, column)
+        self.assertEqual(value, "réponse B")
+
+        column_name = 'selection_str'
+        column = self.get_column(column_name)
+        value = converter.record_to_html(
+            self.cr, self.uid, column_name, record, column)
+        self.assertEqual(value, "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?")
+
+class TestHTMLExport(TestBasicExport):
+    def test_html(self):
+        converter = self.get_converter('html')
+
+        input = '<span>span</span>'
+        value = converter(input)
+        self.assertEqual(value, input)
+
+class TestDatetimeExport(TestBasicExport):
+    def setUp(self):
+        super(TestDatetimeExport, self).setUp()
+        # set user tz to known value
+        Users = self.registry('res.users')
+        Users.write(self.cr, self.uid, self.uid, {
+            'tz': 'Pacific/Niue'
+        }, context=None)
+
+    def test_date(self):
+        converter = self.get_converter('date')
+
+        value = converter('2011-05-03')
+
+        # default lang/format is US
+        self.assertEqual(value, '05/03/2011')
+
+    def test_datetime(self):
+        converter = self.get_converter('datetime')
+
+        value = converter('2011-05-03 11:12:13')
+
+        # default lang/format is US
+        self.assertEqual(value, '05/03/2011 00:12:13')
+
+    def test_custom_format(self):
+        converter = self.get_converter('datetime')
+        converter2 = self.get_converter('date')
+        opts = {'format': 'MMMM d'}
+
+        value = converter('2011-03-02 11:12:13', options=opts)
+        value2 = converter2('2001-03-02', options=opts)
+        self.assertEqual(
+            value,
+            'March 2'
+        )
+        self.assertEqual(
+            value2,
+            'March 2'
+        )
+
+class TestDurationExport(TestBasicExport):
+    def setUp(self):
+        super(TestDurationExport, self).setUp()
+        # needs to have lang installed otherwise falls back on en_US
+        self.registry('res.lang').load_lang(self.cr, self.uid, 'fr_FR')
+
+    def test_negative(self):
+        converter = self.get_converter('float', 'duration')
+
+        with self.assertRaises(ValueError):
+            converter(-4)
+
+    def test_missing_unit(self):
+        converter = self.get_converter('float', 'duration')
+
+        with self.assertRaises(ValueError):
+            converter(4)
+
+    def test_basic(self):
+        converter = self.get_converter('float', 'duration')
+
+        result = converter(4, {'unit': 'hour'}, {'lang': 'fr_FR'})
+        self.assertEqual(result, u'4 heures')
+
+        result = converter(50, {'unit': 'second'}, {'lang': 'fr_FR'})
+        self.assertEqual(result, u'50 secondes')
+
+    def test_multiple(self):
+        converter = self.get_converter('float', 'duration')
+
+        result = converter(1.5, {'unit': 'hour'}, {'lang': 'fr_FR'})
+        self.assertEqual(result, u"1 heure 30 minutes")
+
+        result = converter(72, {'unit': 'second'}, {'lang': 'fr_FR'})
+        self.assertEqual(result, u"1 minute 12 secondes")
+
+class TestRelativeDatetime(TestBasicExport):
+    # not sure how a test based on "current time" should be tested. Even less
+    # so as it would mostly be a test of babel...
+
+    def setUp(self):
+        super(TestRelativeDatetime, self).setUp()
+        # needs to have lang installed otherwise falls back on en_US
+        self.registry('res.lang').load_lang(self.cr, self.uid, 'fr_FR')
+
+    def test_basic(self):
+        converter = self.get_converter('datetime', 'relative')
+        t = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
+
+        result = converter(t, context={'lang': 'fr_FR'})
+        self.assertEqual(result, u"il y a 1 heure")
diff --git a/openerp/addons/test_converter/tests/test_vectors/image b/openerp/addons/test_converter/tests/test_vectors/image
new file mode 100644 (file)
index 0000000..889c905
Binary files /dev/null and b/openerp/addons/test_converter/tests/test_vectors/image differ
diff --git a/openerp/addons/test_converter/tests/test_vectors/pdf b/openerp/addons/test_converter/tests/test_vectors/pdf
new file mode 100644 (file)
index 0000000..6ae543d
Binary files /dev/null and b/openerp/addons/test_converter/tests/test_vectors/pdf differ
diff --git a/openerp/addons/test_converter/tests/test_vectors/pptx b/openerp/addons/test_converter/tests/test_vectors/pptx
new file mode 100644 (file)
index 0000000..f798aca
Binary files /dev/null and b/openerp/addons/test_converter/tests/test_vectors/pptx differ
diff --git a/openerp/addons/test_exceptions/__init__.py b/openerp/addons/test_exceptions/__init__.py
new file mode 100644 (file)
index 0000000..fe44871
--- /dev/null
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+import models
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_exceptions/__openerp__.py b/openerp/addons/test_exceptions/__openerp__.py
new file mode 100644 (file)
index 0000000..fc104f1
--- /dev/null
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+{
+    'name': 'test-exceptions',
+    'version': '0.1',
+    'category': 'Tests',
+    'description': """A module to generate exceptions.""",
+    'author': 'OpenERP SA',
+    'maintainer': 'OpenERP SA',
+    'website': 'http://www.openerp.com',
+    'depends': ['base'],
+    'data': ['view.xml', 'ir.model.access.csv'],
+    'installable': True,
+    'auto_install': False,
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_exceptions/ir.model.access.csv b/openerp/addons/test_exceptions/ir.model.access.csv
new file mode 100644 (file)
index 0000000..56f99ef
--- /dev/null
@@ -0,0 +1,2 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+access_test_exceptions_model,access_test_exceptions_model,model_test_exceptions_model,,1,1,1,1
diff --git a/openerp/addons/test_exceptions/models.py b/openerp/addons/test_exceptions/models.py
new file mode 100644 (file)
index 0000000..a22c6ea
--- /dev/null
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+import openerp
+
+class m(openerp.osv.osv.Model):
+    """ This model exposes a few methods that will raise the different
+        exceptions that must be handled by the server (and its RPC layer)
+        and the clients.
+    """
+    _name = 'test.exceptions.model'
+
+    def generate_except_osv(self, cr, uid, ids, context=None):
+        # title is ignored in the new (6.1) exceptions
+        raise openerp.osv.osv.except_osv('title', 'description')
+
+    def generate_except_orm(self, cr, uid, ids, context=None):
+        # title is ignored in the new (6.1) exceptions
+        raise openerp.osv.orm.except_orm('title', 'description')
+
+    def generate_warning(self, cr, uid, ids, context=None):
+        raise openerp.exceptions.Warning('description')
+
+    def generate_redirect_warning(self, cr, uid, ids, context=None):
+        dummy, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'test_exceptions', 'action_test_exceptions')
+        raise openerp.exceptions.RedirectWarning('description', action_id, 'go to the redirection')
+
+    def generate_access_denied(self, cr, uid, ids, context=None):
+        raise openerp.exceptions.AccessDenied()
+
+    def generate_access_error(self, cr, uid, ids, context=None):
+        raise openerp.exceptions.AccessError('description')
+
+    def generate_exc_access_denied(self, cr, uid, ids, context=None):
+        raise Exception('AccessDenied')
+
+    def generate_undefined(self, cr, uid, ids, context=None):
+        self.surely_undefined_symbol
+
+
+    def generate_except_osv_safe_eval(self, cr, uid, ids, context=None):
+        self.generate_safe_eval(cr, uid, ids, self.generate_except_osv, context)
+
+    def generate_except_orm_safe_eval(self, cr, uid, ids, context=None):
+        self.generate_safe_eval(cr, uid, ids, self.generate_except_orm, context)
+
+    def generate_warning_safe_eval(self, cr, uid, ids, context=None):
+        self.generate_safe_eval(cr, uid, ids, self.generate_warning, context)
+
+    def generate_redirect_warning_safe_eval(self, cr, uid, ids, context=None):
+        self.generate_safe_eval(cr, uid, ids, self.generate_redirect_warning, context)
+
+    def generate_access_denied_safe_eval(self, cr, uid, ids, context=None):
+        self.generate_safe_eval(cr, uid, ids, self.generate_access_denied, context)
+
+    def generate_access_error_safe_eval(self, cr, uid, ids, context=None):
+        self.generate_safe_eval(cr, uid, ids, self.generate_access_error, context)
+
+    def generate_exc_access_denied_safe_eval(self, cr, uid, ids, context=None):
+        self.generate_safe_eval(cr, uid, ids, self.generate_exc_access_denied, context)
+
+    def generate_undefined_safe_eval(self, cr, uid, ids, context=None):
+        self.generate_safe_eval(cr, uid, ids, self.generate_undefined, context)
+
+
+    def generate_safe_eval(self, cr, uid, ids, f, context):
+        globals_dict = { 'generate': lambda *args: f(cr, uid, ids, context) }
+        openerp.tools.safe_eval.safe_eval("generate()", mode='exec', globals_dict=globals_dict)
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_exceptions/view.xml b/openerp/addons/test_exceptions/view.xml
new file mode 100644 (file)
index 0000000..4d8e820
--- /dev/null
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+
+        <record id="view_test_exceptions_model" model="ir.ui.view">
+            <field name="name">Test exceptions</field>
+            <field name="model">test.exceptions.model</field>
+            <field name="arch" type="xml">
+                <form string="Test exceptions">
+                    <label string="Each button generates a specific exception on the server. The text on the right is the expected representation of the exception when displayed on the client. Button marked with a '*' use safe_eval()."/>
+                    <separator string="" colspan="8"/>
+                    <group colspan="8" col="8">
+                    <group colspan="4" col="8">
+                      <group colspan="8" col="8">
+                          <button name="generate_except_osv" string="except_osv" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Warning-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_except_orm" string="except_orm" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Warning-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_warning" string="Warning" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Warning-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_redirect_warning" string="RedirectWarning" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Warning-description-redirection button"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_access_denied" string="AccessDenied" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Access denied-traceback"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_access_error" string="AccessError" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Access rights error-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_exc_access_denied" string="Exc AccessDenied" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Access denied-traceback"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_undefined" string="Undefined" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Server error-traceback"/>
+                      </group>
+                    </group>
+                    <group colspan="4" col="8">
+                      <group colspan="8" col="8">
+                          <button name="generate_except_osv_safe_eval" string="except_osv*" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Warning-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_except_orm_safe_eval" string="except_orm*" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Warning-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_warning_safe_eval" string="Warning*" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Warning-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_redirect_warning_safe_eval" string="RedirectWarning*" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Warning-description-redirection button"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_access_denied_safe_eval" string="AccessDenied*" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Access denied-traceback"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_access_error_safe_eval" string="AccessError*" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Access rights error-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_exc_access_denied_safe_eval" string="Exc AccessDenied*" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Access denied-traceback"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_undefined_safe_eval" string="Undefined*" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Server error-traceback"/>
+                      </group>
+                    </group>
+                    </group>
+                </form>
+           </field>
+        </record>
+
+        <record id="action_test_exceptions" model="ir.actions.act_window">
+            <field name="name">Test exceptions</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="res_model">test.exceptions.model</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">form</field>
+            <field name="target">new</field>
+        </record>
+
+        <menuitem icon="STOCK_PREFERENCES" id="base.menu_tests" name="Tests" sequence="1000000"/>
+
+        <menuitem id="menu_test_exceptions" parent="base.menu_tests" name="Test exceptions"/>
+
+        <menuitem id="menu_test_exceptions_leaf"
+            name="Test exceptions"
+            action="action_test_exceptions"
+            parent="menu_test_exceptions"/>
+    </data>
+</openerp>
diff --git a/openerp/addons/test_impex/__init__.py b/openerp/addons/test_impex/__init__.py
new file mode 100644 (file)
index 0000000..bff786c
--- /dev/null
@@ -0,0 +1 @@
+import models
diff --git a/openerp/addons/test_impex/__openerp__.py b/openerp/addons/test_impex/__openerp__.py
new file mode 100644 (file)
index 0000000..2b52b6a
--- /dev/null
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+{
+    'name': 'test-import-export',
+    'version': '0.1',
+    'category': 'Tests',
+    'description': """A module to test import/export.""",
+    'author': 'OpenERP SA',
+    'maintainer': 'OpenERP SA',
+    'website': 'http://www.openerp.com',
+    'depends': ['base'],
+    'data': ['ir.model.access.csv'],
+    'installable': True,
+    'auto_install': False,
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_impex/ir.model.access.csv b/openerp/addons/test_impex/ir.model.access.csv
new file mode 100644 (file)
index 0000000..fd0f737
--- /dev/null
@@ -0,0 +1,26 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+access_export_boolean,access_export_boolean,model_export_boolean,,1,1,1,1
+access_export_integer,access_export_integer,model_export_integer,,1,1,1,1
+access_export_float,access_export_float,model_export_float,,1,1,1,1
+access_export_decimal,access_export_decimal,model_export_decimal,,1,1,1,1
+access_export_string_bounded,access_export_string_bounded,model_export_string_bounded,,1,1,1,1
+access_export_string_required,access_export_string_required,model_export_string_required,,1,1,1,1
+access_export_string,access_export_string,model_export_string,,1,1,1,1
+access_export_date,access_export_date,model_export_date,,1,1,1,1
+access_export_datetime,access_export_datetime,model_export_datetime,,1,1,1,1
+access_export_text,access_export_text,model_export_text,,1,1,1,1
+access_export_selection,access_export_selection,model_export_selection,,1,1,1,1
+access_export_selection_function,access_export_selection_function,model_export_selection_function,,1,1,1,1
+access_export_many2one,access_export_many2one,model_export_many2one,,1,1,1,1
+access_export_one2many,access_export_one2many,model_export_one2many,,1,1,1,1
+access_export_many2many,access_export_many2many,model_export_many2many,,1,1,1,1
+access_export_function,access_export_function,model_export_function,,1,1,1,1
+access_export_one2many_child,access_export_one2many_child,model_export_one2many_child,,1,1,1,1
+access_export_one2many_multiple,access_export_one2many_multiple,model_export_one2many_multiple,,1,1,1,1
+access_export_one2many_multiple_child,access_export_one2many_multiple_child,model_export_one2many_multiple_child,,1,1,1,1
+access_export_one2many_child_1,access_export_one2many_child_1,model_export_one2many_child_1,,1,1,1,1
+access_export_one2many_child_2,access_export_one2many_child_2,model_export_one2many_child_2,,1,1,1,1
+access_export_many2many_other,access_export_many2many_other,model_export_many2many_other,,1,1,1,1
+access_export_selection_withdefault,access_export_selection_withdefault,model_export_selection_withdefault,,1,1,1,1
+access_export_one2many_recursive,access_export_one2many_recursive,model_export_one2many_recursive,,1,1,1,1
+access_export_unique,access_export_unique,model_export_unique,,1,1,1,1
diff --git a/openerp/addons/test_impex/models.py b/openerp/addons/test_impex/models.py
new file mode 100644 (file)
index 0000000..8c76850
--- /dev/null
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+from openerp.osv import orm, fields
+
+def selection_fn(obj, cr, uid, context=None):
+    return list(enumerate(["Corge", "Grault", "Wheee", "Moog"]))
+
+def function_fn(model, cr, uid, ids, field_name, arg, context):
+    return dict((id, 3) for id in ids)
+def function_fn_write(model, cr, uid, id, field_name, field_value, fnct_inv_arg, context):
+    """ just so CreatorCase.export can be used
+    """
+    pass
+
+models = [
+    ('boolean', fields.boolean()),
+    ('integer', fields.integer()),
+    ('float', fields.float()),
+    ('decimal', fields.float(digits=(16, 3))),
+    ('string.bounded', fields.char('unknown', size=16)),
+    ('string.required', fields.char('unknown', size=None, required=True)),
+    ('string', fields.char('unknown', size=None)),
+    ('date', fields.date()),
+    ('datetime', fields.datetime()),
+    ('text', fields.text()),
+    ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux"), (4, '')])),
+    ('selection.function', fields.selection(selection_fn)),
+    # just relate to an integer
+    ('many2one', fields.many2one('export.integer')),
+    ('one2many', fields.one2many('export.one2many.child', 'parent_id')),
+    ('many2many', fields.many2many('export.many2many.other')),
+    ('function', fields.function(function_fn, fnct_inv=function_fn_write, type="integer")),
+    # related: specialization of fields.function, should work the same way
+    # TODO: reference
+]
+for name, field in models:
+    attrs = {
+        '_name': 'export.%s' % name,
+        '_columns': {
+            'const': fields.integer(),
+            'value': field
+        },
+        '_defaults': {'const': 4},
+        'name_get': (lambda self, cr, uid, ids, context=None:
+            [(record.id, "%s:%s" % (self._name, record.value))
+             for record in self.browse(cr, uid, ids, context=context)]),
+        'name_search': (lambda self, cr, uid, name, operator, context=None:
+                self.name_get(cr, uid,
+                    self.search(cr, uid, [['value', operator, int(name.split(':')[1])]])
+                    , context=context)
+                if isinstance(name, basestring) and name.split(':')[0] == self._name
+                else [])
+    }
+    NewModel = type(
+        'Export%s' % ''.join(section.capitalize() for section in name.split('.')),
+        (orm.Model,),
+        attrs)
+
+class One2ManyChild(orm.Model):
+    _name = 'export.one2many.child'
+    # FIXME: orm.py:1161, fix to name_get on m2o field
+    _rec_name = 'value'
+
+    _columns = {
+        'parent_id': fields.many2one('export.one2many'),
+        'str': fields.char('unknown', size=None),
+        'value': fields.integer()
+    }
+    def name_get(self, cr, uid, ids, context=None):
+        return [(record.id, "%s:%s" % (self._name, record.value))
+            for record in self.browse(cr, uid, ids, context=context)]
+    def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
+        return (self.name_get(cr, user,
+                self.search(cr, user, [['value', operator, int(name.split(':')[1])]])
+                , context=context)
+            if isinstance(name, basestring) and name.split(':')[0] == self._name
+            else [])
+
+class One2ManyMultiple(orm.Model):
+    _name = 'export.one2many.multiple'
+
+    _columns = {
+        'parent_id': fields.many2one('export.one2many.recursive'),
+        'const': fields.integer(),
+        'child1': fields.one2many('export.one2many.child.1', 'parent_id'),
+        'child2': fields.one2many('export.one2many.child.2', 'parent_id'),
+    }
+    _defaults = { 'const': 36 }
+
+class One2ManyChildMultiple(orm.Model):
+    _name = 'export.one2many.multiple.child'
+    # FIXME: orm.py:1161, fix to name_get on m2o field
+    _rec_name = 'value'
+
+    _columns = {
+        'parent_id': fields.many2one('export.one2many.multiple'),
+        'str': fields.char('unknown', size=None),
+        'value': fields.integer()
+    }
+    def name_get(self, cr, uid, ids, context=None):
+        return [(record.id, "%s:%s" % (self._name, record.value))
+            for record in self.browse(cr, uid, ids, context=context)]
+class One2ManyChild1(orm.Model):
+    _name = 'export.one2many.child.1'
+    _inherit = 'export.one2many.multiple.child'
+class One2ManyChild2(orm.Model):
+    _name = 'export.one2many.child.2'
+    _inherit = 'export.one2many.multiple.child'
+
+class Many2ManyChild(orm.Model):
+    _name = 'export.many2many.other'
+    # FIXME: orm.py:1161, fix to name_get on m2o field
+    _rec_name = 'value'
+
+    _columns = {
+        'str': fields.char('unknown', size=None),
+        'value': fields.integer()
+    }
+    def name_get(self, cr, uid, ids, context=None):
+        return [(record.id, "%s:%s" % (self._name, record.value))
+            for record in self.browse(cr, uid, ids, context=context)]
+    def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
+        return (self.name_get(cr, user,
+                    self.search(cr, user, [['value', operator, int(name.split(':')[1])]])
+                    , context=context)
+                if isinstance(name, basestring) and name.split(':')[0] == self._name
+                else [])
+
+class SelectionWithDefault(orm.Model):
+    _name = 'export.selection.withdefault'
+
+    _columns = {
+        'const': fields.integer(),
+        'value': fields.selection([(1, "Foo"), (2, "Bar")]),
+    }
+    _defaults = {
+        'const': 4,
+        'value': 2,
+    }
+
+class RecO2M(orm.Model):
+    _name = 'export.one2many.recursive'
+
+    _columns = {
+        'value': fields.integer(),
+        'child': fields.one2many('export.one2many.multiple', 'parent_id')
+    }
+
+class OnlyOne(orm.Model):
+    _name = 'export.unique'
+
+    _columns = {
+        'value': fields.integer(),
+    }
+    _sql_constraints = [
+        ('value_unique', 'unique (value)', "The value must be unique"),
+    ]
diff --git a/openerp/addons/test_impex/tests/__init__.py b/openerp/addons/test_impex/tests/__init__.py
new file mode 100644 (file)
index 0000000..8cab56f
--- /dev/null
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+from . import test_export, test_import, test_load
+
+fast_suite = [
+]
+
+checks = [
+    test_export,
+    test_import,
+    test_load,
+]
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_impex/tests/contacts.json b/openerp/addons/test_impex/tests/contacts.json
new file mode 100644 (file)
index 0000000..4f06503
--- /dev/null
@@ -0,0 +1 @@
+[["Wood y Wood Pecker", "", "Snow Street, 25", "Kainuu", "Finland", "Supplier", "1", "0", "1", ""], ["Roger Pecker", "Default", "Snow Street, 27", "Kainuu", "Finland", "Supplier", "1", "0", "0", "Wood y Wood Pecker"], ["Sharon Pecker", "Shipping", "Snow Street, 28", "Kainuu", "Finland", "Supplier", "1", "0", "0", "Wood y Wood Pecker"], ["Thomas Pecker", "Contact", "Snow Street, 27", "Kainuu", "Finland", "Supplier", "1", "0", "0", "Wood y Wood Pecker"], ["Norseman Roundabout", "", "Atonium Street, 45a", "Brussels", "Belgium", "Supplier", "1", "0", "1", ""], ["Yvan Holiday", "Invoice", "Atonium Street, 45b", "Brussels", "Belgium", "Supplier", "1", "0", "0", "Norseman Roundabout"], ["Jack Unsworth", "Contact", "Atonium Street, 45a", "Brussels", "Belgium", "Supplier", "1", "0", "0", "Norseman Roundabout"]]
diff --git a/openerp/addons/test_impex/tests/contacts_big.json b/openerp/addons/test_impex/tests/contacts_big.json
new file mode 100644 (file)
index 0000000..5dfe77e
--- /dev/null
@@ -0,0 +1 @@
+[["88117cfa", "", "aa48ae52@df356352.com", ""], ["54455583", "0770708969", "75ce07b4@8c061041.com", ""], ["a4f9804e", "0794216989", "6a994598@402c461b.com", ""], ["0cfd7c89", "0788771669", "f2598da7@15f7036b.com", ""], ["220bfe63", "0767248573", "30d7e3f3@993ab043.com", ""], ["33bc0e31", "0747111853", "e86b7718@36a11f08.com", ""], ["ad3d1c39", "0707461083", "17ddff88@de2d370e.com", ""], ["06ba223e", "0793743830", "6c77d328@247685b0.com", ""], ["d457d407", "0723226290", "f497c7a2@c39adaaf.com", ""], ["b24f01d4", "0785727376", "e5c03c2c@be7b89f7.com", ""], ["666bbc2f", "0726730949", "1fbcb995@6b02d029.com", ""], ["8e29a5f9", "0769331825", "17771bb9@4e36debc.com", ""], ["330ee602", "0760467697", "86af9f82@bd3cdfba.com", ""], ["bcd48752", "0747166758", "ccc4c255@05124d59.com", ""], ["4a16c054", "0776729581", "c87cc075@6d84e9ae.com", ""], ["c065ff62", "0708945599", "41391787@d4c3369a.com", ""], ["8636e303", "0798878005", "520e2cee@3506cf0e.com", ""], ["0bdecf04", "0766303691", "7fb38fdd@46f90ec4.com", ""], ["e1ac6628", "0797335906", "5483d5cf@3372b5ea.com", ""], ["f0a24042", "0774112437", "4e99fd6b@7f727830.com", ""], ["41f18eb6", "0757100173", "f904520a@4d42ffde.com", ""], ["c4a8cde7", "", "aa344d15@3dd9bb6f.com", ""], ["abd14d72", "0778342908", "ed9cb7e3@c6fdb61c.com", ""], ["44c65e49", "0711762677", "ef6952c5@a9cd6711.com", ""], ["a3238267", "0758374338", "203081cd@e1263e29.com", ""], ["412069c4", "0745736692", "f2b989ed@67ac8c23.com", ""], ["b332577b", "0783480568", "4382789d@e1dd2623.com", ""], ["84fccf60", "", "ec2987d5@d777ea94.com", ""], ["c633db24", "0780843363", "9c6a9a39@3ba1bc58.com", ""], ["833bdb8f", "", "0c53568d@170c6826.com", ""], ["c6496a37", "0702644587", "808dc444@d3c87550.com", ""], ["3a9cf735", "0732254904", "825cb1f5@9599a25c.com", ""], ["d3bd7567", "0750010894", "3dd53f94@381f30ff.com", ""], ["ec9d38fa", "0722807920", "a63d63c9@9fc07187.com", ""], ["9e858edf", "0759964893", "ee8fc817@4969b884.com", ""], ["3820033b", "0713040363", "846b31dd@1fde871f.com", ""], ["78e3f6fa", "0713215706", "3a1bd069@a2165e02.com", ""], ["833bf332", "0732488435", "e962f524@7a67d172.com", ""], ["ebe0d200", "0791879047", "34a1610c@e30a9051.com", ""], ["e27d3d81", "0702888121", "a6c52338@56630271.com", ""], ["10423400", "0775217021", "24c5bd9d@3b4bbb75.com", ""], ["923f52a7", "0747670735", "19ee7b6b@fb8077b5.com", ""], ["fe50aca6", "0785899752", "99b1c2e4@30f03fdd.com", ""], ["bdbff3f2", "", "cc5cf567@c13f1531.com", ""], ["6dbbeeb9", "0755330628", "33efb58d@1ac1966a.com", ""], ["f88681ce", "", "4d01aa9a@461b4345.com", ""], ["8ff958c9", "0728877017", "c7327902@eae323df.com", ""], ["d8651703", "0711929482", "6aed493b@44b94bb2.com", ""], ["fcf4863f", "0721975645", "c8171bbf@2cefedab.com", ""], ["614949ae", "0708626227", "c54a3aca@bc635fa8.com", ""], ["7b0767ab", "0773868355", "2e5e7b82@bde16366.com", ""], ["ea0fb610", "0714907110", "3781b5b3@6ade96c7.com", ""], ["1833ee8b", "0754159809", "a1fd1724@0a180cd8.com", ""], ["277ea017", "0756815282", "ad93976a@022ff737.com", ""], ["67e662d9", "0789105520", "ecb672e5@df0e020e.com", ""], ["18f2288c", "", "25de55ef@a03da5ef.com", ""], ["32dcbf86", "0738646453", "88e37cac@ec308293.com", ""], ["242eb33b", "0702264598", "d132623d@6a8e65f4.com", ""], ["f2f8e50a", "0731234118", "10bdb9d0@a1dec378.com", ""], ["c414145d", "0723184859", "465e6323@083bdb6f.com", ""], ["1a6d51be", "0782912489", "7f69541f@5e17335f.com", ""], ["ef35d7bc", "0705243855", "0a8f6885@8a236f11.com", ""], ["ac48d3c2", "0714165417", "aa9c4199@1ab6a42c.com", ""], ["5ba7effd", "0731285368", "e7b91cfb@18ab2ca6.com", ""], ["c8349e83", "0711248806", "bf08adeb@72d14356.com", ""], ["ccb97edd", "", "b64dc525@fff1e0b6.com", ""], ["f6b196e8", "0754457697", "915a9cb0@75e21eed.com", ""], ["7a22e438", "0750066621", "4c2ba953@96df5597.com", ""], ["ffb4ecaf", "0791152499", "d9159dc2@6bdedc89.com", ""], ["64d655fb", "0756407008", "498110df@dfc1445a.com", ""], ["f27b441d", "0734603187", "4a23bbf1@ef4d212f.com", ""], ["2405cb10", "0761979851", "92b8250a@125bbd04.com", ""], ["fce58db4", "", "f6dfbd77@704c66d0.com", ""], ["2b16a16b", "0715217717", "d22db762@46605453.com", ""], ["4fbb5479", "0788391407", "e41e70f2@c575eeed.com", ""], ["5929e7ad", "0730836883", "38a50e2f@0cbd9fc0.com", ""], ["9fab30be", "0758240568", "f8422724@eef7fb6f.com", ""], ["57161fa2", "", "913ed6fb@1f9a591c.com", ""], ["787471f3", "0729709798", "45ed74ea@4209730d.com", ""], ["fce02cc8", "0717080949", "0430d0fa@09917e4b.com", ""], ["e5745b08", "", "2e337c3a@038a6c53.com", ""], ["bba2eeb4", "0754696079", "37c75466@05cbd7d1.com", ""], ["f072a1b0", "0774261408", "605c6350@5a7cc516.com", ""], ["95d2b9c0", "", "8935e406@c23ff41c.com", ""], ["8ac4ea3a", "0714072646", "734cfea9@fb198808.com", ""], ["5a523460", "", "2be9f139@0cf11f87.com", ""], ["5b10ec25", "0732185274", "098c0cf3@3676b22d.com", ""], ["8aa42976", "", "2942960c@1ae8ae04.com", ""], ["cdb88ecb", "0758961947", "0ea31305@77af1e9a.com", ""], ["0bd54356", "0784831852", "c437852e@c8e319ee.com", ""], ["1d1768af", "0793590304", "d66c02aa@649e09d1.com", ""], ["c19a3d43", "0749800307", "cf8fc137@761da752.com", ""], ["9c81b4c9", "", "ab84e443@09f1f4ea.com", ""], ["8811594d", "", "53698008@bcc6d45a.com", ""], ["97a8c2d1", "0715236942", "cf88f0ab@fa3f7a28.com", ""], ["fc7d6b5f", "0770870220", "b0e2ee90@41460794.com", ""], ["a158270a", "", "aaab6cf4@d31f8c47.com", ""], ["20bef3ca", "0744837638", "fca8c63c@8dae6b0a.com", ""], ["424b3e69", "", "7d2bb9c4@856ce530.com", ""], ["8ecc1d0a", "", "6df56a62@deac5fbf.com", ""], ["7e1f1b8c", "0795180887", "18d2a24d@a931a1cb.com", ""], ["4546b364", "0771517872", "77ac1ab3@3cd24c25.com", ""], ["a44b23b2", "", "55e75fcb@e20b079d.com", ""], ["90c52454", "0713222790", "e83be6ad@faaa1dcc.com", ""], ["f5fe091e", "0740461816", "fc4b6360@c84d9776.com", ""], ["01052d30", "0790737321", "421a7451@c44558f7.com", ""], ["a65634f3", "0733738433", "a7d57f5f@4f0007cc.com", ""], ["960aae3c", "", "3e2338e3@97fa70e8.com", ""], ["590858c2", "0736114828", "59cec23c@fb9704f4.com", ""], ["75725ca0", "0777217002", "5c7b484a@8c386315.com", ""], ["a16cf653", "0732018015", "a105f736@87a6c14b.com", ""], ["1ee991c1", "0759093690", "1cc3c001@d70dae0e.com", ""], ["48434804", "", "d3cee127@7dbf4217.com", ""], ["edfd871e", "0757718444", "d63d5c26@81eaeb47.com", ""], ["4fcc9a1c", "0738617045", "b114a376@9bd5a931.com", ""], ["b2001895", "0782251612", "a8868ffb@8114353b.com", ""], ["d61261f8", "0717555195", "ac613ffe@d4ee0d8c.com", ""], ["6327118e", "0739171459", "e047cf55@4920c1d3.com", ""], ["f6b14b7a", "0737868032", "418ed43d@b549551d.com", ""], ["22bc0aa9", "0759663409", "90494f38@f50ab03d.com", ""], ["3145a855", "0788697773", "8a0870bd@744b0c6b.com", ""], ["72f043d6", "0780961318", "859d6c8c@d6a29fe3.com", ""], ["98544981", "0707687449", "53a75fab@6ad934ef.com", ""], ["535ddbe3", "0751302080", "5d5dbab7@9b651502.com", ""], ["c3e5f1ac", "", "62955f00@74d50e7c.com", ""], ["6470f36e", "0776617461", "5f6d81a4@e535baa8.com", ""], ["ed39f4ae", "0766428270", "c73a731a@186ebf0c.com", ""], ["6aa1b596", "0720348914", "67d7e38b@5798f6cc.com", ""], ["f5a778ee", "0773231447", "5919b978@2768de0c.com", ""], ["9d530c2b", "0776812615", "4609c18c@aeca697d.com", ""], ["89a13571", "0744825918", "766ddb77@7dfc129d.com", ""], ["90dd9419", "0722654005", "22bef5d1@74801d84.com", ""], ["0d69e61d", "0779495819", "fa2fce6e@d3b9debf.com", ""], ["0a64ee31", "0741511488", "f48455f9@0163e8a5.com", ""], ["87f3e7fd", "0792428108", "a7763cb9@ae893b19.com", ""], ["99a66411", "0785175788", "3abf4513@c90ddc54.com", ""], ["d4d56088", "0748950722", "2dfc3b17@4af80b7c.com", ""], ["1f001808", "0733396761", "59ae4761@17a1bad6.com", ""], ["2d5cac56", "0744393937", "709feac1@1c16f3b0.com", ""], ["d0acb4ea", "0770269511", "66221771@3ff30570.com", ""], ["4f57869d", "0706718397", "1cd2ed99@0ba6e1d3.com", ""], ["a2e66c44", "", "753da3f0@41f8ab02.com", ""], ["843dc71f", "0757529013", "016ff59d@6c1f9655.com", ""], ["c314a28e", "0767460679", "cd2ff99c@8b178d14.com", ""], ["f0d6631d", "0719558518", "0725dfc6@8ce4d6ff.com", ""], ["aa522bd8", "0735252785", "57d3edd6@5eb1bc66.com", ""], ["3f69f804", "0717000930", "6ca23f02@d169a50e.com", ""], ["cc98acfa", "0796356276", "19579c3b@39e71005.com", ""], ["9e6dbdc2", "0769660138", "6349577f@fba33845.com", ""], ["fba0618b", "0758705886", "ae7a9890@e776386f.com", ""], ["f36d563f", "0744525165", "c76184f9@5dccdf5b.com", ""], ["d2df6db1", "0783917910", "f7726c1a@c9e9b839.com", ""], ["8645707d", "0724081473", "842f2369@be456fbb.com", ""], ["95b4296a", "0782845149", "af879705@e8e7bd93.com", ""], ["345ba50c", "0792384168", "ca3ccb91@faf01bf8.com", ""], ["e05de824", "0707660173", "79f09a3d@3deafa86.com", ""], ["de837ee8", "0772243198", "b25275dc@f057ff78.com", ""], ["ae9f1402", "0704963747", "253d5995@6c771579.com", ""], ["259c5532", "0781521724", "cec33738@874afdb1.com", ""], ["1bc82378", "0748931231", "7b3feccd@cd205def.com", ""], ["c73869a0", "0728684210", "790c4825@22921089.com", ""], ["725a20ee", "0716516714", "a5852a47@85846987.com", ""], ["f975f39e", "0732891142", "aea8acea@a6feaa85.com", ""], ["d0fbcd0b", "0753684058", "78f9d7f2@49082702.com", ""], ["d41da511", "0765105657", "bca42072@50aa0f9d.com", ""], ["42be8058", "0776465871", "15d0f988@805b0cb0.com", ""], ["269eda62", "0705841239", "67dc4d59@51389f5b.com", ""], ["2ffc5c2c", "0732437371", "c3b76c4c@9772ea3b.com", ""], ["258e1957", "0768636603", "476831de@98414c35.com", ""], ["a18a68f6", "", "136d0b0e@15864440.com", ""], ["56b9197d", "0734391703", "ab36de76@d7a2bc96.com", ""], ["e1acdbc5", "0790910000", "867827b4@fdffc111.com", ""], ["21e48ef2", "", "4a1bbb5c@29b1ac3a.com", ""], ["aec1a0ea", "0749338116", "64dbedc8@6e81a330.com", ""], ["b1afbf53", "0701600188", "5f1e3fcc@0c6cba23.com", ""], ["bc7e214f", "0725934639", "369caec7@ba1df297.com", ""], ["dd059d1e", "0774510615", "bac60ace@a354f53c.com", ""], ["cbe822f4", "0745859979", "01bebc6e@735bdad1.com", ""], ["961d21e8", "0741358098", "9348702e@ce9c1230.com", ""], ["c8ae2583", "", "3682409d@a15621c4.com", ""], ["c326d443", "0784986501", "0a505045@96938424.com", ""], ["3e51bb13", "0764438578", "b713c235@8e350663.com", ""], ["ad38c924", "0772825196", "ce8d0f19@b2d4ad9f.com", ""], ["65bd5021", "", "3f6318d5@91f006ed.com", ""], ["efd2705b", "0770828242", "736fe829@7c30bc29.com", ""], ["6ebdfef1", "0710559084", "e035b963@a163fb9d.com", ""], ["021358ef", "0711112569", "ecc20e51@547a7cc2.com", ""], ["548d9a59", "0790101525", "d42aa9de@6f91f22e.com", ""], ["262f96db", "0780153799", "094258c8@e03d9795.com", ""], ["a548913a", "0779869470", "6a6999ca@ca0a88d1.com", ""], ["e7beec03", "0796588859", "5e9940fa@eca859cc.com", ""], ["ae5a8077", "", "5ab3a073@18a45934.com", ""], ["eae22551", "0747781534", "6b04366d@7653fc99.com", ""], ["2fb9da57", "0799726898", "895646f6@2a66fe2e.com", ""], ["51d83f2f", "0704387855", "0d2ea467@62feebfc.com", ""], ["3da4bc6d", "0786428049", "db6a479a@37899df4.com", ""], ["c08101a4", "0751956133", "67852281@0bb68d55.com", ""], ["b5f8c156", "0754878556", "bad4eea7@4d5a817a.com", ""], ["ded80b77", "0772054646", "ebb2cd45@54262aec.com", ""], ["7b086cfd", "0776426442", "3b503434@d46b44fe.com", ""], ["7a64e1e8", "0740165572", "e9d50a03@38375fce.com", ""], ["ceaa584b", "0791796518", "b5839977@cfe3ae10.com", ""], ["9c4a8683", "0790187277", "f8e4fe02@02fc1038.com", ""], ["193f99b7", "", "915c1574@50758bc3.com", ""], ["6fb6c30a", "0720403051", "f4bcd3ca@a8ab8216.com", ""], ["9486800f", "0723518103", "a3886d22@6600cdda.com", ""], ["6e09f6bd", "0707095889", "868ffc60@46eb3135.com", ""], ["da1bd13c", "0756382253", "f34abc2c@d18ec367.com", ""], ["3910ae7d", "0786894789", "a8a751a5@9ddf61c7.com", ""], ["32e5be0c", "0738208660", "eab918a8@aeb7a8fe.com", ""], ["c6582498", "0744023415", "b893479d@9ad5c44f.com", ""], ["0c59f520", "", "d552d0fe@8675edcc.com", ""], ["3458ee7a", "", "c1145be6@4043406c.com", ""], ["91fad8e7", "0701771702", "a13640e5@2370e9a4.com", ""], ["2230118a", "0792290165", "529b9444@17c21068.com", ""], ["2df4bff7", "0783219548", "99a9af11@8e5a539d.com", ""], ["0a752397", "0723553802", "b094b6c9@2d8fb162.com", ""], ["918f92a0", "0790485640", "3984cea2@0750289f.com", ""], ["ff811166", "0756833896", "960e71f3@6ded22f4.com", ""], ["6578cd85", "0791455734", "f7e56f2a@35a68ac6.com", ""], ["fd21c33b", "0794847916", "23ae4c63@51515e19.com", ""], ["a12e519d", "0700174532", "d3b4264d@0c9d62f1.com", ""], ["96827dcd", "0780494940", "d70d9ab7@df66a83c.com", ""], ["8cd46009", "", "a8addc7c@c899c5f7.com", ""], ["982fb2ee", "0778368795", "b99de484@d262779b.com", ""], ["dbe5ea51", "0756105541", "ad90b51c@3f1af6d8.com", ""], ["3d963533", "0761709058", "85b32c2c@d99341a7.com", ""], ["064f2df0", "0791023088", "45d63e8f@ce0852ad.com", ""], ["e93edb9d", "0713294122", "b9b82603@a226aaba.com", ""], ["a94ada15", "0796377896", "5dd00c2d@794bcc18.com", ""], ["7de0f3db", "0748976042", "7714652c@3ef8b9d5.com", ""], ["6ff485d2", "0774873001", "77b5b11c@675bf1f7.com", ""], ["e472c175", "0761539211", "dc30792d@00a0ef22.com", ""], ["7a467ead", "0734394099", "e8d5a021@93926f82.com", ""], ["87749969", "0780899950", "dfe454aa@94a8fd44.com", ""], ["c24c8b06", "0777380273", "98dc8782@26872819.com", ""], ["a059abab", "0753758924", "581281a2@890e7bc6.com", ""], ["3b85bdd1", "0784193150", "cf3c82aa@e5df6c1b.com", ""], ["7cbc188d", "0704027570", "c6444b77@8ab56b47.com", ""], ["12ff649b", "0755744514", "a7ce16ba@c054924a.com", ""], ["cc77e0c9", "0724057991", "4e2473d1@4aca6e0e.com", ""], ["026e42e1", "0741456583", "09906c46@e32bed7b.com", ""], ["9e6550e3", "0740725000", "27ddd3ff@c0891335.com", ""], ["700be741", "0715655544", "bcc93b4c@de8854d5.com", ""], ["f15ce066", "0770746966", "aff2e8a9@19f37ff3.com", ""], ["d56faa79", "0780216109", "7119d43e@10e506f6.com", ""], ["d0d97e5c", "0799577373", "9c131514@a5f6fdee.com", ""], ["d3320f24", "0789091392", "e9b8d4a6@e2c9fa38.com", ""], ["c9b3e28b", "0753027473", "d4c85b77@26952326.com", ""], ["5f81787f", "0789741665", "ca7dbbcc@97fc237d.com", ""], ["dd720b39", "0799154501", "9cc0df66@70c2f5d7.com", ""], ["f584f902", "", "38a024f0@27413ff3.com", ""], ["bb37782d", "0788379130", "01ef864a@191fed62.com", ""], ["f6964d05", "0732597248", "32d25b28@6ae6e905.com", ""], ["cfdc9a87", "", "f453b3f6@4ccf20a3.com", ""], ["2c6f8592", "0773589537", "d71824f7@91dd37aa.com", ""], ["ba101360", "0748932503", "674abbfc@81b3b946.com", ""], ["3b35863b", "0759392115", "c249bcc4@e650abe8.com", ""], ["610da7a5", "0712310446", "f4a25391@d7759c5f.com", ""], ["1557e96a", "0796582698", "4aaf8b75@99c557d5.com", ""], ["98519b81", "", "a5050e55@917cd419.com", ""], ["ed820127", "0754946950", "dd82c2c1@7b1ab977.com", ""], ["b78147ee", "0765494006", "53795132@dd38aec7.com", ""], ["3d4859e8", "0782874657", "67b89627@d2269d32.com", ""], ["ce2366c6", "0719814269", "c6200859@0788dca5.com", ""], ["fba51219", "", "b1d107dd@b664bba3.com", ""], ["7469e033", "0742202666", "9cbedb4b@31ba3795.com", ""], ["20c151e6", "0746146553", "90851479@e8a420a4.com", ""], ["14499acc", "0731779256", "c5c930ab@8de927e9.com", ""], ["71297ace", "0771197563", "1bf4547d@f5c8f5b3.com", ""], ["ea630ed8", "0712073290", "245f14c9@9470fedc.com", ""], ["2c54ae4c", "0767896178", "76f988c7@07ade1eb.com", ""], ["e143bc1c", "0798189886", "68717fc3@c4ca3101.com", ""], ["1626281f", "0782558075", "d87d65f6@585b48b9.com", ""], ["3df7b85d", "", "4ef416c6@0e39f805.com", ""], ["101c2c3a", "0754160899", "c77a5ff2@e3dbfd0f.com", ""], ["0e433acb", "", "ce18b2e0@18a803fe.com", ""], ["b454958d", "0758649506", "daadf940@35a7b0e0.com", ""], ["54dd80e3", "", "499da17c@7a6a8e16.com", ""], ["c26e8c0b", "0705458229", "816d4c62@f196940f.com", ""], ["d6c7c2fb", "0712740449", "eb0f3861@028419f0.com", ""], ["ca236785", "0710117939", "74d89d0f@9681f6e2.com", ""], ["03e89273", "0770985481", "a767a13e@202e1c7e.com", ""], ["df528ea6", "0715236243", "4451e8a7@ab9e9cfc.com", ""], ["b369c3a1", "0720792641", "af8e60fb@affbf045.com", ""], ["0e3b3dcb", "0726931283", "b6c80dd6@377de27c.com", ""], ["72f56e12", "0749900046", "dfd0d53e@a04c2e07.com", ""], ["0b616a06", "0788777560", "e520ec34@5a88ce36.com", ""], ["7dcd72da", "0727744886", "c7ecb909@efc93b54.com", ""], ["b6749523", "0755931358", "76b2e187@777e9047.com", ""], ["349f347f", "0772053293", "aed364ad@8f6c1755.com", ""], ["b0a24d58", "0737012558", "b013a7fd@a6d27266.com", ""], ["924dcf44", "0761967844", "539d93ff@4e1741bd.com", ""], ["90781369", "0745086457", "bae1c94b@df7075b3.com", ""], ["7bbf6e08", "0762583195", "c54b4a41@f08e61ec.com", ""], ["696d9ce3", "0753759660", "9be913a2@efd894d9.com", ""], ["32dea399", "0730020519", "b76d0594@9127aaf5.com", ""], ["4aada6ab", "0716703652", "1172d8f6@70029987.com", ""], ["c2b450aa", "0786756810", "05fa85d3@6305e8e2.com", ""], ["f7220cb8", "0776056046", "b14c6af3@ebfcf7d3.com", ""], ["7da05a35", "0741271883", "abe23d1a@35fc89a5.com", ""], ["b277cd48", "0703025387", "ed28c019@feb1fe78.com", ""], ["55a16321", "0740030335", "a3db681b@de92450b.com", ""], ["646ff9aa", "0777457443", "ea0704a8@d6c557b9.com", ""], ["557c863d", "0752298537", "c412a0a1@4db33621.com", ""], ["324da6d3", "", "61e7386a@d4a90843.com", ""], ["144835ab", "", "70744ba9@000ee58a.com", ""], ["eda25157", "0734918749", "efdbfb3e@83133e46.com", ""], ["bf5d4b09", "0712427566", "4b13f413@18c93b1c.com", ""], ["c3745e8a", "0723463789", "e5ccf0c5@00118e4a.com", ""], ["11c0a9da", "0778743010", "865392aa@1c8da31d.com", ""], ["7d0eac7d", "0701997984", "f2990151@e8d055f3.com", ""], ["e701488a", "0712599226", "f0d54316@037ea274.com", ""], ["acac2543", "0771603428", "73cc7388@981dc1a0.com", ""], ["82f371b0", "0758909949", "6ebe0478@2b14af27.com", ""], ["896bb3a8", "", "83158633@aa3fe414.com", ""], ["9ce45cf4", "", "38772abe@078a889f.com", ""], ["fcb5a33a", "0769530741", "29a4e798@effcb6ef.com", ""], ["116d4f92", "0786255405", "e78e003a@cf7ebbc9.com", ""], ["35e5bd6c", "0700694228", "399e4cd6@eb7a9b08.com", ""], ["71df9408", "0798109987", "91433ee1@b3240820.com", ""], ["294e2aa4", "0700947677", "abca0340@4cd61d52.com", ""], ["9553d892", "", "3af85bbd@90ee53d6.com", ""], ["bbcdb933", "0773940915", "de196282@bf4432fe.com", ""], ["3ccbc198", "0742527356", "014618cd@78b4016f.com", ""], ["100d9496", "0786481835", "df9a72cc@123af588.com", ""], ["57cfe177", "0789499193", "f5f0de36@27d22e23.com", ""], ["cdffca88", "0714652673", "4102e3e2@26a0d112.com", ""], ["69c5f300", "0762631167", "14f5940e@fe984955.com", ""], ["819e988a", "0798148053", "b78ed8c4@ef4a213d.com", ""], ["f5c6a92a", "0774167414", "c996df73@8a8a0586.com", ""], ["3d3f9cc9", "0766973558", "f0a43735@47580cbd.com", ""], ["a30056ab", "0787961025", "04256d2d@459d56d8.com", ""], ["7dc0d98e", "0741838356", "2a597e04@f7089dd3.com", ""], ["e19f381b", "", "ce9c237b@4ec0fd84.com", ""], ["34b3c355", "", "77bcc11b@2e791c9a.com", ""], ["4bca2052", "0799612575", "dba5fc2f@600d7b82.com", ""], ["dbc71748", "0726348854", "efc05aff@df67c9ee.com", ""], ["6fee2352", "0786598805", "c8bad10b@f550bfa0.com", ""], ["b56c589c", "0722452846", "43e2ed1c@4e233028.com", ""], ["77f4b627", "0795147567", "6f26728c@015d1c78.com", ""], ["e6e1b439", "0720916434", "6cf4f2cd@d0daef5a.com", ""], ["c4e49a4c", "0787210516", "bfc6dbf7@8f315a9f.com", ""], ["3f717509", "0704228943", "7a9fd98d@112a27be.com", ""], ["8c0f2abf", "0739504671", "b2c6a55d@bf7bb207.com", ""], ["d0ec34db", "0745702321", "4a426510@7c7cc4ba.com", ""], ["cfe0f395", "0708421594", "df815b04@d27562ee.com", ""], ["4ab6a603", "0781614226", "8e7498c4@216eb70c.com", ""], ["53727fa1", "0778595217", "0fef75b6@7e3eee41.com", ""], ["1dd3c6c9", "0795888769", "c88ef6ef@9182a8c1.com", ""], ["5dbdf1c2", "0700853188", "8b2128ff@5a2fa2e4.com", ""], ["5cbf9768", "0752068105", "064d2e97@f4631505.com", ""], ["b81ffbce", "0737952148", "ef90cb7e@7f7bb2d3.com", ""], ["6c9426b4", "0765528647", "571533da@097b853a.com", ""], ["76b87a42", "0735376294", "4fab1c8a@1004c035.com", ""], ["cc67d10f", "", "df05e09c@10cdee9b.com", ""], ["91f2c079", "0727155396", "f70f99d0@e56d0e68.com", ""], ["7687c589", "0723819703", "f4a37237@8306ec54.com", ""], ["cc065203", "0777023625", "235ab1ff@ad59efef.com", ""], ["ada410b8", "0795063396", "d58735f0@1cfd2246.com", ""], ["b17fda79", "0761763470", "06024431@3a90405d.com", ""], ["82c9cf46", "0795927937", "cc7d157e@fe591e61.com", ""], ["1229f23f", "0766794577", "196fbca7@2762695d.com", ""], ["41ee3436", "0751875418", "e789384d@1e8c078b.com", ""], ["b59b1ae3", "0706674618", "eaa7394a@9ca599d5.com", ""], ["32a1572d", "0739902105", "076df342@f7257d5d.com", ""], ["87142aaf", "0773843071", "2d1ed5d0@745442e0.com", ""], ["fd302180", "0752363053", "69d00e9f@4361b9c7.com", ""], ["9b129111", "0711208227", "e3f41ba4@230ff3c7.com", ""], ["00c627e0", "0795414341", "878530d9@891a7047.com", ""], ["f9bd3b66", "0754134870", "36b72937@8fe2912f.com", ""], ["d89c11eb", "0760560969", "6ec530b9@d7f30799.com", ""], ["e2b6ef2f", "0711238958", "6250bb8e@23e30295.com", ""], ["811c4293", "", "81aeac97@4083c108.com", ""], ["96c90ed7", "", "19727897@698b879b.com", ""], ["b82a09af", "", "7527ccbc@4e351f9e.com", ""], ["17e61a75", "", "ffdd4947@541dcc9f.com", ""], ["f97fa25d", "0749784894", "8b419430@4ae116d0.com", ""], ["13b9a7da", "0748292813", "1d2e437d@1f7fd137.com", ""], ["e0879369", "0778630426", "fbc7e460@350c1a55.com", ""], ["1a74805d", "0749531731", "f06e561d@ebbec1ed.com", ""], ["eb5db9c6", "0707095397", "ec71e260@1ab509e1.com", ""], ["9da2dc8e", "0717471756", "23418425@2dffbc70.com", ""], ["a6f6f46f", "0703721541", "037414ed@c1759536.com", ""], ["765a5633", "", "4fd61a7f@e9fc7672.com", ""], ["3a717722", "0785655203", "82f8e536@4aab3d98.com", ""], ["34b938b7", "0712389450", "0ce3ed1c@8d5aec4e.com", ""], ["fa4eaa80", "0760264228", "487d193e@86c36a2b.com", ""], ["6ba54ee3", "0737464908", "ee4791dc@6e7e18cc.com", ""], ["3d02d6e8", "", "b2c13ef3@66a3948e.com", ""], ["ee3c7b2e", "", "2c51c4fc@97eaabe3.com", ""], ["943acb8a", "0775102341", "306ebc20@ad6a778a.com", ""], ["e86c14c3", "0705424432", "40c1518d@76aab128.com", ""], ["0d993e76", "0729633289", "a18fcc70@f04ad1c7.com", ""], ["fec3f43d", "0740081762", "e04dd4d1@615c78e6.com", ""], ["90645f43", "0737009757", "d8ed6970@a5baa38b.com", ""], ["723c41aa", "0732060795", "d42e8f6c@9f01caf6.com", ""], ["96235675", "0755010192", "30d98b6f@fad581d8.com", ""], ["1bfda78b", "0736195102", "f0ba5711@24ec477e.com", ""], ["9fde0750", "0737641391", "f73cf50d@108aad37.com", ""], ["4cf28ec2", "0715717575", "f18063da@f0625607.com", ""], ["96824bd2", "0754345078", "f7b7534d@5c430ea4.com", ""], ["57e1a524", "0775075206", "9b963917@f4c93815.com", ""], ["1a1b212d", "0796156113", "0da3bd5f@60d5c0a3.com", ""], ["73267af7", "", "20364468@e070797b.com", ""], ["c4fcd602", "", "c996b53c@8c9e56e7.com", ""], ["23944725", "0756128345", "3f1043b9@4e705983.com", ""], ["8665f6ee", "", "c562cd48@d44eb942.com", ""], ["5178e72f", "0791852636", "bbcae5ee@428b8ba9.com", ""], ["9de66e8d", "0753385146", "55874b2c@f115714e.com", ""], ["de112089", "0734935576", "789b1706@6d2b21b8.com", ""], ["4e8283b9", "0700426636", "081d48fc@797f0558.com", ""], ["081d4271", "", "99829160@285f8fd8.com", ""], ["69dd706e", "0771351294", "906cc914@01cacc1a.com", ""], ["5de2de11", "", "1edc2268@e01bd776.com", ""], ["e823c600", "", "ecda6d9d@74a5da65.com", ""], ["907e7635", "", "14cbd73f@3c5029c4.com", ""], ["d6236d57", "", "39f3d4ae@1b7b8200.com", ""], ["aa2630bb", "", "75535cee@5391b394.com", ""], ["c6cda97f", "0710755065", "ff4ab3b0@e7638240.com", ""], ["b6661f85", "", "067496c8@1c3d2df6.com", ""], ["c95cf777", "", "afa9a3cb@279aa011.com", ""], ["e575e109", "0785713068", "8601eb2f@7d69d9ef.com", ""], ["c4aed4d1", "0797133251", "0ebd0ca0@44b0db13.com", ""], ["d8799ad6", "0766336482", "c8a2db59@fc20467a.com", ""], ["fe041483", "0747021458", "f1341509@a0d4bfcb.com", ""], ["9c8975f4", "", "1adf2f38@6ef39e01.com", ""], ["fc572eb6", "0707977477", "39ce98ea@c55bd59e.com", ""], ["86b7eeac", "0714499420", "19318930@f8cab44a.com", ""], ["d64977ad", "0761071885", "11a682aa@69ca2391.com", ""], ["ea006f73", "", "dfa103d4@ec0cffab.com", ""], ["0137c08a", "", "4e98808d@f9cbe2ee.com", ""], ["4c3ef173", "", "ad18e541@9c961399.com", ""], ["3a90243d", "0702089544", "73d9e758@a8185f82.com", ""], ["552fcf54", "0711153208", "b425511b@1f62462e.com", ""], ["47f20958", "0709287769", "387a2338@24efeaca.com", ""], ["95568c92", "0753062212", "230b16d8@7c47aa21.com", ""], ["4e00e50d", "0705656877", "e0557aa4@17b9f5a3.com", ""], ["6a744fbc", "0752791693", "f1cb0467@fc01b034.com", ""], ["b00241b5", "0707154247", "8fb98d05@46674fd5.com", ""], ["40bbff33", "0798115729", "238dcde2@3a402b74.com", ""], ["2ff0e839", "0734208035", "b85bc3b0@fb7c2ace.com", ""], ["48db2fd2", "0746707903", "21bd36a0@967fc046.com", ""], ["82ce2569", "0712225501", "b578f1ac@ebb9345c.com", ""], ["bc8eb82a", "", "579a208c@3a587c16.com", ""], ["e6c2ceff", "", "cf4f1954@a02d98a5.com", ""], ["36cc5274", "0794054613", "6a6fba68@59b878ab.com", ""], ["a47d46b0", "0779573055", "7e3b995d@8ba62af1.com", ""], ["abc2eea3", "0770887487", "2d0b55d1@9b58d90f.com", ""], ["3aeec47e", "0798128924", "cc01849a@23ae0c8c.com", ""], ["438153a3", "", "fbf57f49@b868a49c.com", ""], ["c2e79acb", "0790684868", "eefb75e0@e0e96a2f.com", ""], ["975b014e", "0762757057", "aa6a37dc@a30b0c72.com", ""], ["e610d666", "0781184369", "4d3f4b20@97d8a401.com", ""], ["73495ac9", "0719719687", "7a018884@c2c13e7c.com", ""], ["9a445855", "0785545967", "2d196afc@ffff6d02.com", ""], ["ce2ab926", "0700868262", "2cd6763f@b6d56901.com", ""], ["c0fcbafb", "0706968744", "8ab42ade@5692f6e3.com", ""], ["0604c52c", "0772596476", "b71e2f2d@31e533dd.com", ""], ["b4fedfd6", "0700359913", "af1c4ae4@fcbb6631.com", ""], ["1e19fa35", "0742671783", "009e471f@8e4f29d9.com", ""], ["6bc8631a", "", "38a6dc3d@900b535a.com", ""], ["09c221a5", "0712627371", "6b081654@a5d250ba.com", ""], ["55d80d5c", "0710423548", "149c66a4@0adbe042.com", ""], ["bc6f4369", "", "28372894@dfa1fbcc.com", ""], ["7aa8a6a4", "0766166352", "9dbb5873@5731fed6.com", ""], ["5c781ef0", "0790623819", "2d629650@b57c316a.com", ""], ["6830c71c", "", "39897b3b@7ee8cde5.com", ""], ["6af8ee98", "0747825215", "5b56cf20@4c81a082.com", ""], ["5a8bcde3", "0763197608", "e69532c0@34630f3b.com", ""], ["8f9037fe", "0726240186", "df9e5814@e4fc4423.com", ""], ["fb13d5b0", "0721173617", "d475522e@c02f6cd8.com", ""], ["9ce57e77", "0774209520", "288a94c3@2a0fba22.com", ""], ["b54fd99f", "0735610926", "061e00aa@34301c70.com", ""], ["fe1a320c", "0776730222", "53770fab@f7c2cd27.com", ""], ["10c3b647", "", "ae78b399@9c814e5f.com", ""], ["d24be5da", "0797581905", "9394a573@bc5e7a67.com", ""], ["e61273a7", "0730217168", "ef553bd3@fbd3732e.com", ""], ["fb6472b6", "", "cdf00170@f2e89868.com", ""], ["b588bcf8", "0715004480", "21505721@1ce9749f.com", ""], ["36ef226a", "0720093875", "293e85b5@c81c476f.com", ""], ["007c51ec", "0770116496", "b1b68fa0@159369c4.com", ""], ["949c6074", "", "6e66fd1b@434382f3.com", ""], ["4e4f11d2", "0762955197", "66d29d82@531b2e83.com", ""], ["758c58d1", "", "9dd36cae@295af2b4.com", ""], ["99be0c22", "0794398863", "0249fb4e@4786d822.com", ""], ["1298fb9d", "0720206151", "d3c59051@5d59357f.com", ""], ["b97f877d", "0779925184", "f6414a10@13ffe42b.com", ""], ["3e653aa1", "0750192219", "b35e6fe5@1b6bf57b.com", ""], ["8fbab13c", "0732153634", "b0a7711e@52ecc07a.com", ""], ["1a3e75c1", "0743946942", "25786349@8c65834c.com", ""], ["6ca639a0", "0717692914", "ad3be222@b10a9147.com", ""], ["3c8dd318", "", "bcccd36e@cc70d14f.com", ""], ["a6d3e7cd", "", "aa9c8a70@963098a4.com", ""], ["6fc1b3a2", "", "d19b5d6b@934e24f1.com", ""], ["759261f1", "0709242692", "72f51517@87b24cae.com", ""], ["3d7a1d1e", "0715207809", "877c9255@1671ff51.com", ""], ["93278583", "0742390782", "21dca350@2cb6d4ef.com", ""], ["51f96553", "0748353883", "6be176c3@4e96ba7a.com", ""], ["a347fde6", "0741522657", "c133fafa@26d07149.com", ""], ["362bd97e", "", "60ac173a@10183062.com", ""], ["b1a7c0ff", "0764817452", "67e54fec@06bf6f87.com", ""], ["9ee4aa89", "0741377765", "1856ef66@cc9f3fcf.com", ""], ["49a7dbd1", "0783985348", "ce3970b4@ea82bad4.com", ""], ["206b0643", "0767461177", "a897ff67@8adf5df6.com", ""], ["596a240b", "0722068325", "82663950@9a960206.com", ""], ["dc852f2d", "", "381e1687@8bc841b8.com", ""], ["44dbefa1", "0774987561", "391ecd0c@26e9e890.com", ""], ["69e6e40c", "0713319482", "978839d0@5ad63633.com", ""], ["0121d5a7", "0714766437", "8abfc0e0@b6e025bf.com", ""], ["cd5200b5", "0764514081", "6304987a@1d2f7eb9.com", ""], ["536c4831", "", "c0e01f58@cc7a1d56.com", ""], ["31167693", "0712547344", "9e54c7e9@90b5ff9a.com", ""], ["6a240068", "0776430272", "4ae0eabc@345cd527.com", ""], ["c14f594b", "", "7351d1ff@31c0438e.com", ""], ["b290c6f6", "", "9368d514@98711325.com", ""], ["f870b7b6", "0789240289", "4f8593ba@f80fe1b6.com", ""], ["f9c1a49b", "0714414302", "02efd04f@a4f155cd.com", ""], ["51605326", "0704713807", "13a21489@ec8993c8.com", ""], ["88b5f2e2", "0794091627", "a2745c7e@a32c3b1d.com", ""], ["f3b6a422", "", "4e024c4c@022f5e1f.com", ""], ["c2cfc88a", "0761247770", "153b9fe1@9859b8a4.com", ""], ["597f3524", "0761882451", "1982d242@a8f03583.com", ""], ["d694bc4c", "", "864732d7@76277920.com", ""], ["8e859b4a", "", "d16f7f69@0261a0ef.com", ""], ["2d583afa", "", "876b76c6@e55415c1.com", ""], ["1bd3b1ae", "0761041162", "a2ebbee4@9a1b4f54.com", ""], ["a01d3c8b", "0703532365", "d6d7b818@bf578a2a.com", ""], ["6b8ff331", "0705773074", "e1e26a20@144139a8.com", ""], ["bef24803", "0750088813", "fde0039c@4ed43033.com", ""], ["253651f9", "0715013226", "3aebf85d@f7455c19.com", ""], ["34da6189", "0764808908", "03f2bd4a@c73ec94e.com", ""], ["c72f3422", "0754811376", "5941e3ce@769439b1.com", ""], ["1761f923", "0708249762", "20e78457@68b640c2.com", ""], ["a7ddd99c", "0754994431", "ad342d73@622fa530.com", ""], ["1b32b5cb", "0769400739", "510cf15d@1feaf7a5.com", ""], ["2ecd5127", "0788462377", "f2a96f71@c6dadef6.com", ""], ["b2e338ca", "0780233422", "0394850d@c7f808dd.com", ""], ["0999515c", "0708264022", "9214c832@73793633.com", ""], ["b4662820", "0791822790", "378843d9@faad02b6.com", ""], ["b6526e56", "0702391559", "ddd3922e@683a9bc3.com", ""], ["e1d0dfca", "0796434690", "bbea0324@a25f259b.com", ""], ["7507ff74", "0718970953", "957feb53@dd898780.com", ""], ["537fba55", "0712047297", "380f1934@1afa6994.com", ""], ["c03244c8", "0719594534", "d685945b@5f863d81.com", ""], ["7a0b4463", "0726964918", "7dada8d2@632a578b.com", ""], ["01f105bd", "0771635491", "0103cc3d@2c004125.com", ""], ["7c9baaea", "0793949807", "b0aae8e3@23796a56.com", ""], ["05f636c7", "0707511947", "36c7c75f@2d86967f.com", ""], ["71d917a2", "0784386161", "ad18b2df@9366e77f.com", ""], ["d417a204", "", "e3ef2306@48c768ce.com", ""], ["8d6d1bb3", "0705778200", "ee43e84c@f6df11fa.com", ""], ["f4c05035", "0772727935", "c0ced236@b4ea187c.com", ""], ["e6d75e18", "0788952581", "402b9e4d@0b37e829.com", ""], ["1dabeec9", "0727510448", "a0fac12e@0ea007ab.com", ""], ["f71fa168", "0759368909", "2b111301@22da70d4.com", ""], ["23b7c929", "0786848255", "b71bbd84@857f9ec8.com", ""], ["a085c14c", "0740213711", "d32148b7@b67a73ae.com", ""], ["9ac9fa3e", "0701707031", "42cbdd1a@175f318e.com", ""], ["b4e40dc9", "0715864237", "a15c733d@6669b4f7.com", ""], ["94020cfb", "0715193084", "ad45863a@2eaab246.com", ""], ["626c1ee7", "0750957414", "d66ae397@2e895e2f.com", ""], ["1eabc823", "0731567529", "20f8332e@b4b0703c.com", ""], ["e5a98097", "0718031536", "718b7ae2@8504fe2d.com", ""], ["69245c65", "0779488230", "fc645fef@635fbdcf.com", ""], ["247ce107", "0721718937", "2c662519@b0c33ecc.com", ""], ["c4925e4c", "0710565488", "36e178bb@4cb2efd9.com", ""], ["d0ef8f45", "0725811338", "f5404bce@9e12cd0a.com", ""], ["ed80a2b0", "0708451045", "6b9f4393@5ea5c209.com", ""], ["13aab094", "0711289294", "ce0972c1@c202849a.com", ""], ["0154dcda", "0734530650", "e225ca87@12039c6d.com", ""], ["004cecdb", "0741339169", "d85299ff@ce7f5fd2.com", ""], ["bbbcbeb3", "0723560169", "e31d0863@b105423d.com", ""], ["cef7c1a9", "0752786167", "3c76459a@4e492e4a.com", ""], ["c3cb922f", "0779170714", "24e2fd0a@cd086652.com", ""], ["a267897d", "0740299573", "c8ab2622@5d1d5225.com", ""], ["f73c9d67", "0754733442", "433436bc@eaa41a7e.com", ""], ["bb0022c6", "", "016e3dfc@ea644733.com", ""], ["060c6c8f", "0767126800", "24a3b50a@8c364ded.com", ""], ["9f5b5d9a", "0769176462", "20dc7747@ef1ad5b9.com", ""], ["43e67ec3", "0743483082", "8a5f94e8@5e7d39a0.com", ""], ["345e4a33", "0787677047", "3a0c802c@608dbe31.com", ""], ["ec2e7f7d", "0784990090", "23ec5d05@c4bebf51.com", ""], ["e239c87d", "0738679004", "8cfa7063@bcb2bc21.com", ""], ["271fc9e7", "0778470494", "c2d46c92@dcb3dbfc.com", ""], ["3b0585bd", "0795454441", "40772365@1d8f7187.com", ""], ["a1a58875", "0718370412", "2eb10d10@5ec547bd.com", ""], ["4c45c47c", "0739708464", "81d5c8ac@06447b51.com", ""], ["e57e8359", "0751620730", "4b2b8980@c95b69b5.com", ""], ["735270e1", "", "ee37dbee@25a4ff65.com", ""], ["4b44e00f", "0711946373", "0eb6a22a@b320af8f.com", ""], ["8a372c1e", "0770968660", "d6678940@ee818cf4.com", ""], ["88efebf9", "0720205646", "7d6d6bcd@87fecbbb.com", ""], ["6ebf214d", "0780223716", "d28759e2@e323f23f.com", ""], ["2eb46280", "0795896930", "8dae7897@d3854b08.com", ""], ["53bc97c3", "0730645626", "12518a5f@a1fdfaca.com", ""], ["fec1e211", "0754874523", "40655f66@4897f637.com", ""], ["1b8ef046", "0731698966", "cbed8d9a@893a95be.com", ""], ["70099e91", "0725267567", "b75efdd0@8dcf7f96.com", ""], ["00234cba", "0748766899", "9088d159@a1a1f4c1.com", ""], ["35ff23c6", "0747870199", "c961250c@d8a06d24.com", ""], ["9e7417f8", "0746801322", "ba7e50e4@cae69ff0.com", ""], ["3428f8d3", "", "56c21910@48bf2959.com", ""], ["f575e8a6", "0734183553", "4953d608@e2885866.com", ""], ["ee701421", "0762346436", "0eee7492@884b62a5.com", ""], ["197c760a", "0741186587", "8e5f6e2b@ea3cb8da.com", ""], ["691a46aa", "0708195042", "cf89f958@dfa6105e.com", ""], ["b03db786", "0716744355", "3f1c117f@a6c83666.com", ""], ["4be4d96b", "", "48195284@40467121.com", ""], ["dfcd1ada", "0783073475", "9785f27d@1df650f2.com", ""], ["4490c17c", "0786052205", "8e4bab0c@b98b8c4c.com", ""], ["f4e991f7", "0717102345", "e5a0a4fa@0483a00c.com", ""], ["b15003cd", "0771528694", "e012bb67@b1463416.com", ""], ["95e2a50d", "0796484412", "508dbc66@25ef9b72.com", ""], ["c2003313", "", "8856d03c@72d7544b.com", ""], ["276b4bc0", "0789810903", "ef87caba@2741b9cb.com", ""], ["15da925e", "0748450892", "656949ba@78234623.com", ""], ["77e171e3", "0786551610", "a86b4d19@110baa19.com", ""], ["9d9d0504", "0758342738", "0dc0e069@bb5e721e.com", ""], ["f4def247", "0798052097", "e66df9a7@af41f423.com", ""], ["c3ea412f", "0706797533", "4e28af08@8a754fb6.com", ""], ["53812400", "", "6c6215f1@45e33532.com", ""], ["369bfda8", "", "45cf4bdb@6d0980f6.com", ""], ["dbd3b821", "0765939770", "f448784d@35d292f5.com", ""], ["142f350f", "", "41c305cd@fdf34481.com", ""], ["8fcd1626", "0770444423", "323d955b@41ff7d99.com", ""], ["8789a603", "0751524803", "359bc02a@72af402c.com", ""], ["0a675687", "0789901250", "2323249a@619f78c5.com", ""], ["fb011bf6", "0750579472", "7152dcf1@668f96db.com", ""], ["a623d8fd", "0733289400", "482d64cd@8a4e2926.com", ""], ["6d961e75", "", "6fe4f5e1@e9239485.com", ""], ["4f39910e", "0746456899", "e85969c9@cca744af.com", ""], ["81a834a8", "0731458518", "8ffce3c8@8e7ae609.com", ""], ["70472837", "0775526068", "5dbfd52d@0d829cb6.com", ""], ["2d882041", "0708561610", "e5853dfb@b2187974.com", ""], ["deaf54bc", "", "a9dc3872@2506f15e.com", ""], ["a7226533", "0760964433", "2490dcc2@de364340.com", ""], ["36377c3b", "0759911971", "93465967@b06cdaa6.com", ""], ["31724c44", "0757697274", "b04d8ef1@544a5467.com", ""], ["51806270", "0742044114", "417f3dca@cab99b90.com", ""], ["e9b2295d", "", "d9e360f5@4d87b309.com", ""], ["0dee89bb", "0712052963", "4700d9dc@8b9161ce.com", ""], ["8a4b9e30", "0755847836", "b3d261a8@60e7e6d9.com", ""], ["f8f7bc44", "", "ffd58a0e@e40c3e44.com", ""], ["c5d5d713", "0790256137", "35f89e39@5893f2c1.com", ""], ["90f0cbd9", "0737155772", "6d8e7f05@40038b63.com", ""], ["eef1d1a9", "0742179171", "065c13a8@634d0446.com", ""], ["55faa2a4", "0748611843", "22382d4c@d11eb075.com", ""], ["f7913ace", "", "40be2255@400d8d4e.com", ""], ["0d47fd15", "0797162254", "06ec4142@00a16805.com", ""], ["906a5fb1", "0703201953", "4d249ff5@55ffe749.com", ""], ["4b5010c3", "0741317092", "25d81157@6027b9e1.com", ""], ["f79b697f", "0773396997", "2e116cf9@9a6bfcba.com", ""], ["c6d2d0f0", "", "a83108a9@40159f26.com", ""], ["da24f341", "0775328597", "4e7592d1@70f4d9c1.com", ""], ["b20edca2", "0789450137", "d326baf5@1baafa6e.com", ""], ["3e11a76a", "0776368949", "15a351bc@e4b229ff.com", ""], ["3e2fbdc3", "0717744150", "4ebbd18a@de822931.com", ""], ["7e6c9549", "0750887467", "2c5034ed@23402dc1.com", ""], ["9536e16d", "0713928189", "e6cd0a97@d20db0c5.com", ""], ["8358cf78", "0735864901", "4ea98e6a@e1052078.com", ""], ["d12cef0a", "0730971666", "a7f6061d@b6f2822f.com", ""], ["cc2ee54b", "0778562618", "61917c3e@032bcdb5.com", ""], ["21ef6091", "0763511033", "166d075e@522d8051.com", ""], ["f6b7a396", "0772929913", "13ae6bf9@c44fbe8b.com", ""], ["042555cf", "", "b11161b5@143fc047.com", ""], ["9739c45e", "0792544143", "9a57d50c@7545618b.com", ""], ["aad5cccf", "0781328164", "59e7123e@488f0065.com", ""], ["b886493a", "0785224849", "d6499e6f@0688b326.com", ""], ["2c99ee07", "0748501224", "1e582961@799a7165.com", ""], ["72545b19", "0715248877", "637c5be0@c9d62b4b.com", ""], ["6f038b04", "0717198089", "fa325ae7@624b634c.com", ""], ["4f8e0b2a", "0764277265", "cb4a5eab@35f7346a.com", ""], ["03fdeddd", "0721789994", "c0476d68@4d5cd7bd.com", ""], ["89d74e2d", "0760756190", "d6594d52@5dee56e4.com", ""], ["c24acb5f", "0781827089", "3a1ea265@8bd8e1cc.com", ""], ["d1bf9230", "0722180686", "9e582b1e@e8351d46.com", ""], ["9dd2e88d", "0711860229", "56658ca4@132ac241.com", ""], ["a07824cb", "0741457266", "76f4cfea@f25c3adc.com", ""], ["f3897859", "0710503175", "8f23c250@4540617e.com", ""], ["c09a14dd", "", "e9c464ac@f76cfe06.com", ""], ["c9a758e4", "0790149404", "b5fbb29e@2ef70cf1.com", ""], ["dd56f5d4", "0709704673", "8b24db91@663c1804.com", ""], ["1ccf7b2e", "0728115684", "d7610e72@e4a8b643.com", ""], ["32351dff", "0748120068", "3939d0ab@8fdf1e7d.com", ""], ["8c4053c5", "0714496140", "420781c7@8ccbf30e.com", ""], ["ce266357", "0784144894", "de2e719c@22b747ed.com", ""], ["3257e91e", "0783497860", "09f03ee1@a98f49a0.com", ""], ["0180d6f1", "0743825253", "ec4bf604@a0b51abc.com", ""], ["5360eee3", "0719368035", "bb270d1d@984f9d39.com", ""], ["d40e19a5", "", "5ccc6f37@d6870955.com", ""], ["a429a2e0", "", "9372812a@b1676aa3.com", ""], ["45dfc0c2", "0709417666", "fed1b00e@bf665131.com", ""], ["a173558c", "0758991067", "3f706fd9@301776a7.com", ""], ["ca87b3af", "0781867475", "98155b79@adcd5507.com", ""], ["036da0ce", "0778243834", "66467ae3@686faa6d.com", ""], ["c5ddfc00", "0750665015", "4e0a5924@9d77bef0.com", ""], ["40b39118", "0765997451", "7bdfe708@a3cee04d.com", ""], ["a5de4669", "0761205980", "bfac2446@e1515b33.com", ""], ["a62448b2", "0739220943", "3da472cc@73d9e74b.com", ""], ["3b04bf59", "0711824354", "b6ac5778@42772967.com", ""], ["ab56dd06", "0791996063", "3949b2d8@b08c4c5f.com", ""], ["9820b796", "0724164512", "ecad52ea@81041046.com", ""], ["894e5af9", "0706784563", "4b2ee10d@821e0a2e.com", ""], ["dd06ec54", "0782812141", "a2e91578@875cac30.com", ""], ["802f32a7", "0749216707", "8e7171df@898e4f66.com", ""], ["ff30e79e", "", "e09f32d2@e8bed799.com", ""], ["7070b09d", "0707078844", "bfb66733@308be7e8.com", ""], ["fde6085d", "0712529236", "c4750b1e@05266810.com", ""], ["87f4aafd", "0739190648", "1cb92876@1cf2f6df.com", ""], ["94a88efa", "0775446056", "f0ce2f87@410508db.com", ""], ["20f56909", "0740230843", "55997ce7@82f437b1.com", ""], ["1ee26c6c", "0713640395", "6357a25b@fb48f794.com", ""], ["ee1ba135", "0789826448", "097f6423@892f4d97.com", ""], ["7fa6baf5", "0752582669", "6e49ac6d@7012d5a1.com", ""], ["35fc8c1d", "0789186620", "0abe0f3f@2435673a.com", ""], ["0e7434e8", "0791384205", "96c88dc9@8e26852c.com", ""], ["6a899564", "", "65902813@1686d624.com", ""], ["90d1935e", "0756326458", "8ddc4a04@a5572b9d.com", ""], ["f8c69667", "", "c827107a@93f105af.com", ""], ["b2c511ea", "0749010920", "24bb2d69@f4287f85.com", ""], ["6ecc592e", "0788769694", "4c132913@931ce84d.com", ""], ["6722b9db", "0794262991", "52bed401@35003685.com", ""], ["6874d990", "0748452679", "b5dca8b6@97c1b768.com", ""], ["72740c63", "0721748699", "9e10c395@a98c8e1c.com", ""], ["ee0d78e0", "0795469696", "56d8a295@d6ef7c7e.com", ""], ["d5bcb2aa", "0741284724", "34c858aa@5f49689d.com", ""], ["5909a519", "0744679678", "88409af7@e19f4243.com", ""], ["f265acfa", "0792029151", "e34d1172@0e9bba98.com", ""], ["94a5602e", "0709917575", "0464515e@552c3013.com", ""], ["d0d483ad", "0764326930", "86b8c70f@a6382da0.com", ""], ["b9155373", "0725007352", "e3223601@f5a902c8.com", ""], ["600e630d", "", "f4e4d66b@bf8aa87c.com", ""], ["fe12bd24", "0772663541", "8b8b1297@1e02a9bf.com", ""], ["fc11b86a", "", "65f3c1ac@375002e4.com", ""], ["110d8fa4", "0775226701", "3d955f6b@ce7da885.com", ""], ["d3105be1", "0779779352", "ed2be0eb@ccc7485e.com", ""], ["fda61a0b", "0720274167", "6becb3e3@4deb6681.com", ""], ["17f8be17", "0799234608", "73f14970@cb957e59.com", ""], ["e9516a1d", "0760461815", "8ad30006@8df0fd1a.com", ""], ["60177106", "0760558146", "dc029d5c@b6ef2fd2.com", ""], ["3450866c", "0711798090", "cda58109@c2e25542.com", ""], ["06e26393", "0735961481", "305bcf19@6c3ec630.com", ""], ["708af001", "0728438353", "048b067d@80d72f50.com", ""], ["c40ec0a3", "0726279359", "a10d333e@9a89cb7c.com", ""], ["2a2eb847", "0741461630", "d3daf952@7a3408e4.com", ""], ["d01a1813", "0704645200", "f226e03a@f67c7d10.com", ""], ["819cb8a9", "0798337915", "cf70d59b@d1a42e77.com", ""], ["6fa32598", "0701415106", "4a225f04@2534e6a3.com", ""], ["4e441149", "", "456f82ab@ca0b04a5.com", ""], ["f7ff1a4a", "0701515624", "5918544a@4bb3704b.com", ""], ["5738b682", "0724843216", "04c74b30@ed510bac.com", ""], ["f10e2d6a", "0730727806", "415d4081@b1a88a68.com", ""], ["749e83bd", "", "15e0671b@76428121.com", ""], ["3d65b85b", "", "d1eb4053@c363e69a.com", ""], ["a0792a15", "0729947768", "c24f344e@41f87b82.com", ""], ["fb01d572", "0792621720", "9c750d18@7fb1f5ce.com", ""], ["e67acc97", "0789099675", "bdfbe8ff@e2ca97d6.com", ""], ["a9e6cd4b", "0708488791", "1826128f@98d76c9c.com", ""], ["819f95a3", "0788991943", "cf458e5d@44f3c946.com", ""], ["20204d4c", "0714720322", "1efe1d68@5dddd6aa.com", ""], ["a1ea35bb", "0749468567", "480f2ab1@72a338ed.com", ""], ["92ffc165", "0780038157", "4dd11686@daa7a1e3.com", ""], ["4ba3f501", "", "71b12e98@a76b654f.com", ""], ["d7f5285b", "0736359794", "fc0e04a5@dde7291b.com", ""], ["77bfe67f", "0742014858", "b3c8790c@cacf5548.com", ""], ["dbf35774", "0726444745", "2243d1d2@138dfd54.com", ""], ["e9f91a9f", "0760618445", "db7cbba8@4616651c.com", ""], ["04c6be09", "", "9adf3814@5e01afe2.com", ""], ["b57e7ee3", "0722578719", "1736e501@964d3424.com", ""], ["d8114397", "", "6bc5e72a@bc4d7e37.com", ""], ["7ab21681", "", "aa33cd18@d6af8a2b.com", ""], ["290e81e9", "", "4d6f5933@bb9b4aa8.com", ""], ["5a1f2ac9", "0778832081", "53b60c08@8973dcfb.com", ""], ["f8775a07", "0743005567", "3c53f38a@c0918592.com", ""], ["30c7cdb1", "0779422190", "a809f36f@1b3dc9e3.com", ""], ["1c669546", "0793826953", "0b2728a3@f2f4eee2.com", ""], ["229beef7", "", "97621f15@237e6070.com", ""], ["ab8c48d5", "", "8545951c@c7523008.com", ""], ["e4294ab1", "", "6d8bdfd5@e9c1c910.com", ""], ["c9c8fbe2", "0729515527", "bcd2115d@e4500586.com", ""], ["0b6463d3", "0725941332", "e768eb3b@fbd48ad0.com", ""], ["15bbab63", "", "a8fd2422@b236a910.com", ""], ["c181cff0", "0717305018", "fb4e37ed@b611bc36.com", ""], ["03f712cd", "", "a05576b0@e16b8368.com", ""], ["31105e59", "0790432797", "6e4a7e2a@d6902424.com", ""], ["5c868ce0", "0738424344", "36348d0f@342e6bed.com", ""], ["d47bd754", "0793838363", "dd9d97d7@54a56407.com", ""], ["660908ec", "0707537040", "ae9b6681@a9382430.com", ""], ["2c6290a0", "0759084568", "b011def2@e1ac3f5a.com", ""], ["a989f05a", "", "25e59710@79b7f417.com", ""], ["f3c16d3b", "", "ded9819d@a99d7dc3.com", ""], ["1adf7818", "0751467205", "b96aa90a@9791c269.com", ""], ["409cc67e", "0777500182", "5317c827@a2db4c45.com", ""], ["aabd103f", "", "e5d907aa@bcc71e10.com", ""], ["d23bf430", "0777169222", "85e8641e@cc5f3d8b.com", ""], ["69a2879f", "0743889293", "4b5f4141@04205775.com", ""], ["8a3ec274", "0756861440", "ee663794@82c1f4fe.com", ""], ["b4a3cd48", "", "fc399765@64ca060b.com", ""], ["e0ccaed3", "", "6388033c@824e8b90.com", ""], ["c3eee2b9", "", "c0162073@2c2fd2b2.com", ""], ["a73c64c4", "", "f688a2e5@6e30672c.com", ""], ["2d852e73", "0772174434", "7a42d0d9@72486cd3.com", ""], ["a12254f5", "0769384878", "caf08e44@d46ed98b.com", ""], ["f653b74f", "0763997467", "388c7213@1126f95d.com", ""], ["fc93ee15", "0774848627", "beabdaca@2d84271e.com", ""], ["73c04bfd", "0771623831", "8fe53552@31ce4032.com", ""], ["74a5f23f", "0789722672", "82930bac@7dc3e501.com", ""], ["796d3e3d", "0762835594", "1181180a@ec66e2f0.com", ""], ["1bc14fc5", "0704902009", "ed326523@23e42c30.com", ""], ["5c08dcb0", "0765115244", "dd5945cf@f297b86b.com", ""], ["566bf582", "0723354400", "ea731083@ef83ec7c.com", ""], ["f25208d4", "0789906022", "457e0601@4eb796dd.com", ""], ["114eeeb3", "0758751442", "14cfa76b@aee76d12.com", ""], ["2669d4a2", "", "5ea1e2ac@cf638cf0.com", ""], ["6202f1c1", "", "d93e09a6@d3898452.com", ""], ["5126ade5", "", "96601158@cfeca444.com", ""], ["a0cef71f", "", "e9366869@532d79f8.com", ""], ["41bda0e9", "", "c4206b97@966c822f.com", ""], ["a75dc6cc", "", "12906204@892175ee.com", ""], ["79636bcc", "", "c45c9aae@a303f7d9.com", ""], ["e40fb55e", "", "72fe75ec@c1ddfd45.com", ""], ["e0ddb510", "", "ea6c8b14@193b1240.com", ""], ["c7532313", "", "a9361a51@f562d91e.com", ""], ["0158d9f3", "", "1de7b7e6@876beb92.com", ""], ["b2394690", "", "fe1b9570@3cffd682.com", ""], ["79051273", "", "fbb85c72@4532b2d6.com", ""], ["1ea2aeae", "", "6e8d529a@fa5af449.com", ""], ["2c637d3a", "", "b633e7ca@b2e822e5.com", ""], ["ba3b59cc", "", "10e596a4@e2654efc.com", ""], ["631c774b", "", "03b0156e@5f3383bd.com", ""], ["6e4d8e1d", "", "b7026e82@015d71cc.com", ""], ["3d430d86", "", "6af17483@bf92c6e9.com", ""], ["ea26cfde", "", "aeee3cf2@e06f3387.com", ""], ["16596f6f", "", "d1445706@a3282036.com", ""], ["9bc58d49", "", "", ""], ["cc907372", "", "a923bc16@c3febc01.com", ""], ["605dd0be", "", "49daa341@8ee4c07e.com", ""], ["1b1114f0", "", "ec5a8a0b@489a7dd5.com", ""], ["28b0c962", "", "", ""], ["63370e89", "", "44d07879@3c414118.com", ""], ["f3f1db57", "", "", ""], ["ac49009b", "", "e5cd7740@ea261a3b.com", ""], ["9e46dcc0", "", "83a47ee1@a58545dc.com", ""], ["2e95367c", "", "8bee6be7@8906c53f.com", ""], ["ea22c7bb", "", "53b4805b@af24dcf7.com", ""], ["c90f526b", "", "d790585d@8d10cf2a.com", ""], ["7887a62b", "", "7120351b@ff19865b.com", ""], ["64cb3f9d", "", "", ""], ["9e591fa7", "", "c7730953@a714f3a7.com", ""], ["1f16cd07", "", "67bebc78@40ed8809.com", ""], ["287ec13a", "", "9e5e71cb@2bb1c891.com", ""], ["d164c461", "", "1cd6e067@14c6ada1.com", ""], ["36379fd3", "", "72a5809d@9b00b1bb.com", ""], ["9470c307", "", "2816f5ec@451ff875.com", ""], ["1655c648", "", "f06ec4a5@f5ab6d4c.com", ""], ["879040ac", "", "62e235cb@ec8a0c43.com", ""], ["50232cdf", "", "025cd190@2477e789.com", ""], ["5a35f64f", "", "", ""], ["777de661", "", "38640b10@41514086.com", ""], ["705fb8cf", "", "7284860f@b222b1f8.com", ""], ["fcb30c1b", "", "361fde21@1f411552.com", ""], ["079e7250", "", "6bbcefcd@2aeb37af.com", ""], ["4e47023c", "", "d23b6b4d@b0b56474.com", ""], ["1bce884e", "", "1dfab7b8@68a3a0ef.com", ""], ["7088e56a", "", "6570590b@e2a30ae0.com", ""], ["65ce20d2", "", "86106467@58070f61.com", ""], ["68d599aa", "", "e279dc2a@58d99123.com", ""], ["0142c624", "", "9abe7329@cb4a4022.com", ""], ["9e8bcae3", "", "840f16ca@1ed99fc8.com", ""], ["4b854254", "", "fbf12ee9@6499201a.com", ""], ["615d5c95", "", "abce7c9f@ade377af.com", ""], ["d389a4b2", "", "a78f0fc2@a6f959cd.com", ""], ["38be0158", "", "bfa96d44@f363eb7c.com", ""], ["f9e06f7c", "", "542bd29b@97746a9e.com", ""], ["832811a7", "", "9bf9010e@01957fac.com", ""], ["359a78e3", "", "3248a6d3@763ddb24.com", ""], ["27f9817c", "", "07d94000@2aca2301.com", ""], ["013a3bd4", "", "2224822e@fade404f.com", ""], ["a0ef9d39", "", "", ""], ["b024d6ee", "", "0daa6bb1@2ef82615.com", ""], ["a381acc3", "", "bad81f63@dc7acb79.com", ""], ["7610e7b9", "", "59829816@d8a49cb9.com", ""], ["4460acd9", "", "acf4b236@dd8a5e68.com", ""], ["5193bc70", "", "f5620364@f763b237.com", ""], ["9d134fef", "", "df0234b6@ae3b90cf.com", ""], ["8d8c4586", "", "f92d2add@0f93928c.com", ""], ["2d12e299", "", "99162fbe@59bd4569.com", ""], ["bcc4641b", "", "afff34a5@f2e5d247.com", ""], ["5c2243c3", "", "3ef0d599@f34a9ed2.com", ""], ["427913d9", "", "086fe7e8@925e9a12.com", ""], ["3a60c0ec", "", "66c4dfbc@cee34594.com", ""], ["ffca31e8", "", "0a4a245c@68e91e5e.com", ""], ["7df057ab", "", "335cabf9@a17a61b3.com", ""], ["71307e65", "", "c1de44cd@a1add235.com", ""], ["17bf2c39", "", "601cc244@f0efd775.com", ""], ["a8b9e2e1", "", "de175716@d0cfb7a0.com", ""], ["a294ae4d", "", "", ""], ["37859daf", "", "376f0f2b@369a7bcf.com", ""], ["01e1685f", "", "6fed663d@3672e214.com", ""], ["0a32e0c6", "", "0b0791c3@2c33f23e.com", ""], ["5b8ff540", "", "29599ad0@548964c2.com", ""], ["eec1094b", "", "dfa57b8f@80b6e55e.com", ""], ["acf49b4d", "", "4c20a1c0@c6d0e041.com", ""], ["63867c55", "", "", ""], ["873984c8", "", "1cf228b4@b15a1b5f.com", ""], ["ecde582c", "", "cc419b7b@60107818.com", ""], ["039d5e8f", "", "5da65ece@1bb69031.com", ""], ["21f04ac9", "", "8ee84896@25275182.com", ""], ["735349c7", "", "117051ac@3a4fd4d7.com", ""], ["273761c9", "", "0184bda8@198fe950.com", ""], ["200dff3c", "", "cb311031@7c6cc49c.com", ""], ["80a68462", "", "5f3c6275@44263f25.com", ""], ["53ace4f9", "", "733a3c0e@c7fd4969.com", ""], ["484e9625", "", "bfe97c09@d2ef03d6.com", ""], ["b798a822", "", "cd531d09@aa763932.com", ""], ["04cecc11", "", "e173c817@3977a28a.com", ""], ["2a910e01", "", "5e200dad@5cf70a26.com", ""], ["540b1452", "", "c9462faa@cb7652ed.com", ""], ["80261f65", "", "fea8b5bc@e5018e45.com", ""], ["9e4fa7c2", "", "95f3f9c9@f265aefc.com", ""], ["f0227223", "", "702f9660@2006f10b.com", ""], ["500bb986", "", "40a22a5c@ed4055c3.com", ""], ["34c14a29", "", "3d2633f6@206a53a0.com", ""], ["66a868a5", "", "7a2b1e68@9cf601fb.com", ""], ["af8d6986", "", "", ""], ["1fbefa32", "", "e948b9f8@c0fe216c.com", ""], ["ee799cb5", "", "01013bb3@229547a9.com", ""], ["b2ce8d2e", "", "fe3750e1@ee50e669.com", ""], ["ae7c0d5b", "0727494158", "68aad918@674f032f.com", ""], ["1c55c64e", "", "efc9a2bb@1fec90ba.com", ""], ["2eef74a3", "", "238d16ec@14658003.com", ""], ["5ba48a58", "", "c6d52520@65f96971.com", ""], ["e650ca0a", "", "76be4eba@a74dba94.com", ""], ["f977adbb", "0789591713", "68b0f662@c01e961d.com", ""], ["ac083f57", "0718005506", "6443f242@7ed34530.com", ""], ["0c7080de", "0791608592", "4444abf7@63b54852.com", ""], ["599039e0", "0717263596", "34898b46@b248d961.com", ""], ["33656705", "0788380435", "0d6c86a9@0d0b30a4.com", ""], ["f0d6f309", "0766220442", "22d01833@9bba21ab.com", ""], ["7c27f7a0", "0740179768", "69d0ce4f@829116df.com", ""], ["cba407c8", "0745165304", "4e94762c@6033decd.com", ""], ["3a9c0bcd", "0738794804", "ea1ca3ef@bd6258aa.com", ""], ["006b2bde", "0759645406", "822277ef@e36775c4.com", ""], ["fe832f26", "", "", ""], ["03c43ae1", "0776208246", "8e64a53e@d0aefbfb.com", ""], ["4ae6a913", "0798677379", "4affe084@f4ee6d7c.com", ""], ["5d3d3ca1", "0755000779", "0ea61d50@7ffd96e5.com", ""], ["dc58f75f", "0717795899", "119adc66@edbc9bac.com", ""], ["593f7e23", "", "d3161d49@fe2ad83f.com", ""], ["e5075ec4", "0760361665", "2f410a97@1b02d6a2.com", ""], ["a0d86ad4", "0765965949", "0fb55b69@cd1c2b77.com", ""], ["09e6e8a1", "0790479077", "cc542c67@9cb69e36.com", ""], ["77bb6d11", "0720961023", "60ff31ae@d9349e56.com", ""], ["e2f4aed5", "0735608325", "663b0400@671dff5d.com", ""], ["149211a9", "0706545912", "8481da49@233dfe8e.com", ""], ["59a53ba0", "", "3401405f@e1c370ad.com", ""], ["60a4c106", "0746099226", "7003f4bf@6a7770b2.com", ""], ["4483788d", "0728712830", "b30ee032@1694bb85.com", ""], ["ee8a3129", "0712964023", "34ee5c80@c82fb116.com", ""], ["c8bdde49", "", "3ac6c6dd@13bc3322.com", ""], ["65a477be", "0705359300", "a67e1d49@966dedb0.com", ""], ["d1a51add", "", "", ""], ["467392b8", "0765663197", "f878eeb4@7334e9fd.com", ""], ["61b4a1d6", "", "9d7e523e@e334c7d2.com", ""], ["16bbecf4", "0794827711", "39dff936@596fb073.com", ""], ["1cd25515", "0786915799", "d1c9de87@fb823959.com", ""], ["9d653b08", "0783707353", "75f1c3fa@dca1ac44.com", ""], ["cb93adc9", "0757349238", "7c3f098b@cf9bb4fe.com", ""], ["5f879d9e", "0764695115", "0a710345@b99b17e3.com", ""], ["8b840055", "", "", ""], ["d436dacc", "0796095598", "ea120899@3c8f6ea0.com", ""], ["1d0faf93", "0744424802", "3cf588a8@44544be8.com", ""], ["4ee8b8f7", "0764292501", "", ""]]
\ No newline at end of file
diff --git a/openerp/addons/test_impex/tests/test_export.py b/openerp/addons/test_impex/tests/test_export.py
new file mode 100644 (file)
index 0000000..26219b3
--- /dev/null
@@ -0,0 +1,589 @@
+# -*- coding: utf-8 -*-
+import itertools
+import openerp.modules.registry
+import openerp
+
+from openerp.tests import common
+
+
+class CreatorCase(common.TransactionCase):
+    model_name = False
+
+    def __init__(self, *args, **kwargs):
+        super(CreatorCase, self).__init__(*args, **kwargs)
+        self.model = None
+
+    def setUp(self):
+        super(CreatorCase, self).setUp()
+        self.model = self.registry(self.model_name)
+    def make(self, value):
+        id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': value})
+        return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0]
+    def export(self, value, fields=('value',), context=None):
+        record = self.make(value)
+        return self.model._BaseModel__export_row(
+            self.cr, openerp.SUPERUSER_ID, record,
+            [f.split('/') for f in fields],
+            context=context)
+
+class test_boolean_field(CreatorCase):
+    model_name = 'export.boolean'
+
+    def test_true(self):
+        self.assertEqual(
+            self.export(True),
+            [[u'True']])
+    def test_false(self):
+        """ ``False`` value to boolean fields is unique in being exported as a
+        (unicode) string, not a boolean
+        """
+        self.assertEqual(
+            self.export(False),
+            [[u'False']])
+
+class test_integer_field(CreatorCase):
+    model_name = 'export.integer'
+
+    def test_empty(self):
+        self.assertEqual(self.model.search(self.cr, openerp.SUPERUSER_ID, []), [],
+                         "Test model should have no records")
+    def test_0(self):
+        self.assertEqual(
+            self.export(0),
+            [[False]])
+
+    def test_basic_value(self):
+        self.assertEqual(
+            self.export(42),
+            [[u'42']])
+
+    def test_negative(self):
+        self.assertEqual(
+            self.export(-32),
+            [[u'-32']])
+
+    def test_huge(self):
+        self.assertEqual(
+            self.export(2**31-1),
+            [[unicode(2**31-1)]])
+
+class test_float_field(CreatorCase):
+    model_name = 'export.float'
+
+    def test_0(self):
+        self.assertEqual(
+            self.export(0.0),
+            [[False]])
+
+    def test_epsilon(self):
+        self.assertEqual(
+            self.export(0.000000000027),
+            [[u'2.7e-11']])
+
+    def test_negative(self):
+        self.assertEqual(
+            self.export(-2.42),
+            [[u'-2.42']])
+
+    def test_positive(self):
+        self.assertEqual(
+            self.export(47.36),
+            [[u'47.36']])
+
+    def test_big(self):
+        self.assertEqual(
+            self.export(87654321.4678),
+            [[u'87654321.4678']])
+
+class test_decimal_field(CreatorCase):
+    model_name = 'export.decimal'
+
+    def test_0(self):
+        self.assertEqual(
+            self.export(0.0),
+            [[False]])
+
+    def test_epsilon(self):
+        """ epsilon gets sliced to 0 due to precision
+        """
+        self.assertEqual(
+            self.export(0.000000000027),
+            [[False]])
+
+    def test_negative(self):
+        self.assertEqual(
+            self.export(-2.42),
+            [[u'-2.42']])
+
+    def test_positive(self):
+        self.assertEqual(
+            self.export(47.36),
+            [[u'47.36']])
+
+    def test_big(self):
+        self.assertEqual(
+            self.export(87654321.4678), [[u'87654321.468']])
+
+class test_string_field(CreatorCase):
+    model_name = 'export.string.bounded'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.export(""),
+            [[False]])
+    def test_within_bounds(self):
+        self.assertEqual(
+            self.export("foobar"),
+            [[u"foobar"]])
+    def test_out_of_bounds(self):
+        self.assertEqual(
+            self.export("C for Sinking, "
+                        "Java for Drinking, "
+                        "Smalltalk for Thinking. "
+                        "...and Power to the Penguin!"),
+            [[u"C for Sinking, J"]])
+
+class test_unbound_string_field(CreatorCase):
+    model_name = 'export.string'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.export(""),
+            [[False]])
+    def test_small(self):
+        self.assertEqual(
+            self.export("foobar"),
+            [[u"foobar"]])
+    def test_big(self):
+        self.assertEqual(
+            self.export("We flew down weekly to meet with IBM, but they "
+                        "thought the way to measure software was the amount "
+                        "of code we wrote, when really the better the "
+                        "software, the fewer lines of code."),
+            [[u"We flew down weekly to meet with IBM, but they thought the "
+              u"way to measure software was the amount of code we wrote, "
+              u"when really the better the software, the fewer lines of "
+              u"code."]])
+
+class test_text(CreatorCase):
+    model_name = 'export.text'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.export(""),
+            [[False]])
+    def test_small(self):
+        self.assertEqual(
+            self.export("foobar"),
+            [[u"foobar"]])
+    def test_big(self):
+        self.assertEqual(
+            self.export("So, `bind' is `let' and monadic programming is"
+                        " equivalent to programming in the A-normal form. That"
+                        " is indeed all there is to monads"),
+            [[u"So, `bind' is `let' and monadic programming is equivalent to"
+              u" programming in the A-normal form. That is indeed all there"
+              u" is to monads"]])
+
+class test_date(CreatorCase):
+    model_name = 'export.date'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.export(False),
+            [[False]])
+    def test_basic(self):
+        self.assertEqual(
+            self.export('2011-11-07'),
+            [[u'2011-11-07']])
+
+class test_datetime(CreatorCase):
+    model_name = 'export.datetime'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.export(False),
+            [[False]])
+    def test_basic(self):
+        self.assertEqual(
+            self.export('2011-11-07 21:05:48'),
+            [[u'2011-11-07 21:05:48']])
+    def test_tz(self):
+        """ Export ignores the timezone and always exports to UTC
+
+        .. note:: on the other hand, export uses user lang for name_get
+        """
+        # NOTE: ignores user timezone, always exports to UTC
+        self.assertEqual(
+            self.export('2011-11-07 21:05:48', context={'tz': 'Pacific/Norfolk'}),
+            [[u'2011-11-07 21:05:48']])
+
+class test_selection(CreatorCase):
+    model_name = 'export.selection'
+    translations_fr = [
+        ("Qux", "toto"),
+        ("Bar", "titi"),
+        ("Foo", "tete"),
+    ]
+
+    def test_empty(self):
+        self.assertEqual(
+            self.export(False),
+            [[False]])
+
+    def test_value(self):
+        """ selections export the *label* for their value
+        """
+        self.assertEqual(
+            self.export(2),
+            [[u"Bar"]])
+
+    def test_localized_export(self):
+        self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, {
+            'name': u'Français',
+            'code': 'fr_FR',
+            'translatable': True,
+            'date_format': '%d.%m.%Y',
+            'decimal_point': ',',
+            'thousands_sep': ' ',
+        })
+        Translations = self.registry('ir.translation')
+        for source, value in self.translations_fr:
+            Translations.create(self.cr, openerp.SUPERUSER_ID, {
+                'name': 'export.selection,value',
+                'lang': 'fr_FR',
+                'type': 'selection',
+                'src': source,
+                'value': value
+            })
+        self.assertEqual(
+            self.export(2, context={'lang': 'fr_FR'}),
+            [[u'Bar']])
+
+class test_selection_function(CreatorCase):
+    model_name = 'export.selection.function'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.export(False),
+            [[False]])
+
+    def test_value(self):
+        # FIXME: selection functions export the *value* itself
+        self.assertEqual(
+            self.export(1),
+            [[u'1']])
+        self.assertEqual(
+            self.export(3),
+            [[u'3']])
+        # fucking hell
+        self.assertEqual(
+            self.export(0),
+            [[False]])
+
+class test_m2o(CreatorCase):
+    model_name = 'export.many2one'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.export(False),
+            [[False]])
+    def test_basic(self):
+        """ Exported value is the name_get of the related object
+        """
+        integer_id = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        name = dict(self.registry('export.integer').name_get(
+            self.cr, openerp.SUPERUSER_ID,[integer_id]))[integer_id]
+        self.assertEqual(
+            self.export(integer_id),
+            [[name]])
+    def test_path(self):
+        """ Can recursively export fields of m2o via path
+        """
+        integer_id = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        self.assertEqual(
+            self.export(integer_id, fields=['value/.id', 'value/value']),
+            [[unicode(integer_id), u'42']])
+    def test_external_id(self):
+        integer_id = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        # __export__.$class.$id
+        external_id = u'__export__.export_many2one_%d' % integer_id
+        self.assertEqual(
+            self.export(integer_id, fields=['value/id']),
+            [[external_id]])
+
+class test_o2m(CreatorCase):
+    model_name = 'export.one2many'
+    commands = [
+        (0, False, {'value': 4, 'str': 'record1'}),
+        (0, False, {'value': 42, 'str': 'record2'}),
+        (0, False, {'value': 36, 'str': 'record3'}),
+        (0, False, {'value': 4, 'str': 'record4'}),
+        (0, False, {'value': 13, 'str': 'record5'}),
+    ]
+    names = [
+        u'export.one2many.child:%d' % d['value']
+        for c, _, d in commands
+    ]
+
+    def test_empty(self):
+        self.assertEqual(
+            self.export(False),
+            [[False]])
+
+    def test_single(self):
+        self.assertEqual(
+            self.export([(0, False, {'value': 42})]),
+            # name_get result
+            [[u'export.one2many.child:42']])
+
+    def test_single_subfield(self):
+        self.assertEqual(
+            self.export([(0, False, {'value': 42})],
+                        fields=['value', 'value/value']),
+            [[u'export.one2many.child:42', u'42']])
+
+    def test_integrate_one_in_parent(self):
+        self.assertEqual(
+            self.export([(0, False, {'value': 42})],
+                        fields=['const', 'value/value']),
+            [[u'4', u'42']])
+
+    def test_multiple_records(self):
+        self.assertEqual(
+            self.export(self.commands, fields=['const', 'value/value']),
+            [
+                [u'4', u'4'],
+                [u'', u'42'],
+                [u'', u'36'],
+                [u'', u'4'],
+                [u'', u'13'],
+            ])
+
+    def test_multiple_records_name(self):
+        self.assertEqual(
+            self.export(self.commands, fields=['const', 'value']),
+            [[
+                u'4', u','.join(self.names)
+            ]])
+
+    def test_multiple_records_id(self):
+        export = self.export(self.commands, fields=['const', 'value/.id'])
+        O2M_c = self.registry('export.one2many.child')
+        ids = O2M_c.browse(self.cr, openerp.SUPERUSER_ID,
+                           O2M_c.search(self.cr, openerp.SUPERUSER_ID, []))
+        self.assertEqual(
+            export,
+            [
+                ['4', str(ids[0].id)],
+                ['', str(ids[1].id)],
+                ['', str(ids[2].id)],
+                ['', str(ids[3].id)],
+                ['', str(ids[4].id)],
+            ])
+
+    def test_multiple_records_with_name_before(self):
+        self.assertEqual(
+            self.export(self.commands, fields=['const', 'value', 'value/value']),
+            [[ # exports sub-fields of very first o2m
+                u'4', u','.join(self.names), u'4'
+            ]])
+
+    def test_multiple_records_with_name_after(self):
+        self.assertEqual(
+            self.export(self.commands, fields=['const', 'value/value', 'value']),
+            [ # completely ignores name_get request
+                [u'4', u'4', ''],
+                ['', u'42', ''],
+                ['', u'36', ''],
+                ['', u'4', ''],
+                ['', u'13', ''],
+            ])
+
+    def test_multiple_subfields_neighbour(self):
+        self.assertEqual(
+            self.export(self.commands, fields=['const', 'value/str','value/value']),
+            [
+                [u'4', u'record1', u'4'],
+                ['', u'record2', u'42'],
+                ['', u'record3', u'36'],
+                ['', u'record4', u'4'],
+                ['', u'record5', u'13'],
+            ])
+
+    def test_multiple_subfields_separated(self):
+        self.assertEqual(
+            self.export(self.commands, fields=['value/str', 'const', 'value/value']),
+            [
+                [u'record1', u'4', u'4'],
+                [u'record2', '', u'42'],
+                [u'record3', '', u'36'],
+                [u'record4', '', u'4'],
+                [u'record5', '', u'13'],
+            ])
+
+class test_o2m_multiple(CreatorCase):
+    model_name = 'export.one2many.multiple'
+
+    def make(self, value=None, **values):
+        if value is not None: values['value'] = value
+        id = self.model.create(self.cr, openerp.SUPERUSER_ID, values)
+        return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0]
+    def export(self, value=None, fields=('child1', 'child2',), context=None, **values):
+        record = self.make(value, **values)
+        return self.model._BaseModel__export_row(
+            self.cr, openerp.SUPERUSER_ID, record,
+            [f.split('/') for f in fields],
+            context=context)
+
+    def test_empty(self):
+        self.assertEqual(
+            self.export(child1=False, child2=False),
+            [[False, False]])
+
+    def test_single_per_side(self):
+        self.assertEqual(
+            self.export(child1=False, child2=[(0, False, {'value': 42})]),
+            [[False, u'export.one2many.child.2:42']])
+
+        self.assertEqual(
+            self.export(child1=[(0, False, {'value': 43})], child2=False),
+            [[u'export.one2many.child.1:43', False]])
+
+        self.assertEqual(
+            self.export(child1=[(0, False, {'value': 43})],
+                        child2=[(0, False, {'value': 42})]),
+            [[u'export.one2many.child.1:43', u'export.one2many.child.2:42']])
+
+    def test_single_integrate_subfield(self):
+        fields = ['const', 'child1/value', 'child2/value']
+        self.assertEqual(
+            self.export(child1=False, child2=[(0, False, {'value': 42})],
+                        fields=fields),
+            [[u'36', False, u'42']])
+
+        self.assertEqual(
+            self.export(child1=[(0, False, {'value': 43})], child2=False,
+                        fields=fields),
+            [[u'36', u'43', False]])
+
+        self.assertEqual(
+            self.export(child1=[(0, False, {'value': 43})],
+                        child2=[(0, False, {'value': 42})],
+                        fields=fields),
+            [[u'36', u'43', u'42']])
+
+    def test_multiple(self):
+        """ With two "concurrent" o2ms, exports the first line combined, then
+        exports the rows for the first o2m, then the rows for the second o2m.
+        """
+        fields = ['const', 'child1/value', 'child2/value']
+        child1 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
+                  for index, v in zip(itertools.count(), [4, 42, 36, 4, 13])]
+        child2 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
+                  for index, v in zip(itertools.count(10), [8, 12, 8, 55, 33, 13])]
+
+        self.assertEqual(
+            self.export(child1=child1, child2=False, fields=fields),
+            [
+                [u'36', u'4', False],
+                ['', u'42', ''],
+                ['', u'36', ''],
+                ['', u'4', ''],
+                ['', u'13', ''],
+            ])
+        self.assertEqual(
+            self.export(child1=False, child2=child2, fields=fields),
+            [
+                [u'36', False, u'8'],
+                ['', '', u'12'],
+                ['', '', u'8'],
+                ['', '', u'55'],
+                ['', '', u'33'],
+                ['', '', u'13'],
+            ])
+        self.assertEqual(
+            self.export(child1=child1, child2=child2, fields=fields),
+            [
+                [u'36', u'4', u'8'],
+                ['', u'42', ''],
+                ['', u'36', ''],
+                ['', u'4', ''],
+                ['', u'13', ''],
+                ['', '', u'12'],
+                ['', '', u'8'],
+                ['', '', u'55'],
+                ['', '', u'33'],
+                ['', '', u'13'],
+            ])
+
+class test_m2m(CreatorCase):
+    model_name = 'export.many2many'
+    commands = [
+        (0, False, {'value': 4, 'str': 'record000'}),
+        (0, False, {'value': 42, 'str': 'record001'}),
+        (0, False, {'value': 36, 'str': 'record010'}),
+        (0, False, {'value': 4, 'str': 'record011'}),
+        (0, False, {'value': 13, 'str': 'record100'}),
+    ]
+    names = [
+        u'export.many2many.other:%d' % d['value']
+        for c, _, d in commands
+    ]
+
+    def test_empty(self):
+        self.assertEqual(
+            self.export(False),
+            [[False]])
+
+    def test_single(self):
+        self.assertEqual(
+            self.export([(0, False, {'value': 42})]),
+            # name_get result
+            [[u'export.many2many.other:42']])
+
+    def test_single_subfield(self):
+        self.assertEqual(
+            self.export([(0, False, {'value': 42})],
+                        fields=['value', 'value/value']),
+            [[u'export.many2many.other:42', u'42']])
+
+    def test_integrate_one_in_parent(self):
+        self.assertEqual(
+            self.export([(0, False, {'value': 42})],
+                        fields=['const', 'value/value']),
+            [[u'4', u'42']])
+
+    def test_multiple_records(self):
+        self.assertEqual(
+            self.export(self.commands, fields=['const', 'value/value']),
+            [
+                [u'4', u'4'],
+                [u'', u'42'],
+                [u'', u'36'],
+                [u'', u'4'],
+                [u'', u'13'],
+            ])
+
+    def test_multiple_records_name(self):
+        self.assertEqual(
+            self.export(self.commands, fields=['const', 'value']),
+            [[ # FIXME: hardcoded comma, import uses config.csv_internal_sep
+               # resolution: remove configurable csv_internal_sep
+                u'4', u','.join(self.names)
+            ]])
+
+    # essentially same as o2m, so boring
+
+class test_function(CreatorCase):
+    model_name = 'export.function'
+
+    def test_value(self):
+        """ Exports value normally returned by accessing the function field
+        """
+        self.assertEqual(
+            self.export(42),
+            [[u'3']])
diff --git a/openerp/addons/test_impex/tests/test_import.py b/openerp/addons/test_impex/tests/test_import.py
new file mode 100644 (file)
index 0000000..e4e8a6a
--- /dev/null
@@ -0,0 +1,882 @@
+# -*- coding: utf-8 -*-
+import openerp.modules.registry
+import openerp
+
+from openerp.tests import common
+from openerp.tools.misc import mute_logger
+
+def ok(n):
+    """ Successful import of ``n`` records
+
+    :param int n: number of records which should have been imported
+    """
+    return n, 0, 0, 0
+
+def error(row, message, record=None, **kwargs):
+    """ Failed import of the record ``record`` at line ``row``, with the error
+    message ``message``
+
+    :param str message:
+    :param dict record:
+    """
+    return (
+        -1, dict(record or {}, **kwargs),
+        "Line %d : %s" % (row, message),
+        '')
+
+def values(seq, field='value'):
+    return [item[field] for item in seq]
+
+class ImporterCase(common.TransactionCase):
+    model_name = False
+
+    def __init__(self, *args, **kwargs):
+        super(ImporterCase, self).__init__(*args, **kwargs)
+        self.model = None
+
+    def setUp(self):
+        super(ImporterCase, self).setUp()
+        self.model = self.registry(self.model_name)
+
+    def import_(self, fields, rows, context=None):
+        return self.model.import_data(
+            self.cr, openerp.SUPERUSER_ID, fields, rows, context=context)
+    def read(self, fields=('value',), domain=(), context=None):
+        return self.model.read(
+            self.cr, openerp.SUPERUSER_ID,
+            self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context),
+            fields=fields, context=context)
+    def browse(self, domain=(), context=None):
+        return self.model.browse(
+            self.cr, openerp.SUPERUSER_ID,
+            self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context),
+            context=context)
+
+    def xid(self, record):
+        ModelData = self.registry('ir.model.data')
+
+        ids = ModelData.search(
+            self.cr, openerp.SUPERUSER_ID,
+            [('model', '=', record._table_name), ('res_id', '=', record.id)])
+        if ids:
+            d = ModelData.read(
+                self.cr, openerp.SUPERUSER_ID, ids, ['name', 'module'])[0]
+            if d['module']:
+                return '%s.%s' % (d['module'], d['name'])
+            return d['name']
+
+        name = dict(record.name_get())[record.id]
+        # fix dotted name_get results, otherwise xid lookups blow up
+        name = name.replace('.', '-')
+        ModelData.create(self.cr, openerp.SUPERUSER_ID, {
+            'name': name,
+            'model': record._table_name,
+            'res_id': record.id,
+            'module': '__test__'
+        })
+        return '__test__.' + name
+
+class test_ids_stuff(ImporterCase):
+    model_name = 'export.integer'
+
+    def test_create_with_id(self):
+        self.assertEqual(
+            self.import_(['.id', 'value'], [['42', '36']]),
+            error(1, u"Unknown database identifier '42'"))
+    def test_create_with_xid(self):
+        self.assertEqual(
+            self.import_(['id', 'value'], [['somexmlid', '42']]),
+            ok(1))
+        self.assertEqual(
+            'somexmlid',
+            self.xid(self.browse()[0]))
+
+    def test_update_with_id(self):
+        id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': 36})
+        self.assertEqual(
+            36,
+            self.model.browse(self.cr, openerp.SUPERUSER_ID, id).value)
+
+        self.assertEqual(
+            self.import_(['.id', 'value'], [[str(id), '42']]),
+            ok(1))
+        self.assertEqual(
+            [42], # updated value to imported
+            values(self.read()))
+
+    def test_update_with_xid(self):
+        self.import_(['id', 'value'], [['somexmlid', '36']])
+        self.assertEqual([36], values(self.read()))
+
+        self.import_(['id', 'value'], [['somexmlid', '1234567']])
+        self.assertEqual([1234567], values(self.read()))
+
+class test_boolean_field(ImporterCase):
+    model_name = 'export.boolean'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.import_(['value'], []),
+            ok(0))
+
+    def test_exported(self):
+        self.assertEqual(
+            self.import_(['value'], [
+                ['False'],
+                ['True'],
+            ]),
+            ok(2))
+        records = self.read()
+        self.assertEqual([
+            False,
+            True,
+        ], values(records))
+
+    def test_falses(self):
+        self.assertEqual(
+            self.import_(['value'], [
+                [u'0'],
+                [u'no'],
+                [u'false'],
+                [u'FALSE'],
+                [u''],
+            ]),
+            ok(5))
+        self.assertEqual([
+                False,
+                False,
+                False,
+                False,
+                False,
+            ],
+            values(self.read()))
+
+    def test_trues(self):
+        self.assertEqual(
+            self.import_(['value'], [
+                ['off'],
+                ['None'],
+                ['nil'],
+                ['()'],
+                ['f'],
+                ['#f'],
+                # Problem: OpenOffice (and probably excel) output localized booleans
+                ['VRAI'],
+                [u'OFF'],
+            ]),
+            ok(8))
+        self.assertEqual(
+            [True] * 8,
+            values(self.read()))
+
+class test_integer_field(ImporterCase):
+    model_name = 'export.integer'
+
+    def test_none(self):
+        self.assertEqual(
+            self.import_(['value'], []),
+            ok(0))
+
+    def test_empty(self):
+        self.assertEqual(
+            self.import_(['value'], [['']]),
+            ok(1))
+        self.assertEqual(
+            [False],
+            values(self.read()))
+
+    def test_zero(self):
+        self.assertEqual(
+            self.import_(['value'], [['0']]),
+            ok(1))
+        self.assertEqual(
+            self.import_(['value'], [['-0']]),
+            ok(1))
+        self.assertEqual([False, False], values(self.read()))
+
+    def test_positives(self):
+        self.assertEqual(
+            self.import_(['value'], [
+                ['1'],
+                ['42'],
+                [str(2**31-1)],
+                ['12345678']
+            ]),
+            ok(4))
+        self.assertEqual([
+            1, 42, 2**31-1, 12345678
+        ], values(self.read()))
+
+    def test_negatives(self):
+        self.assertEqual(
+            self.import_(['value'], [
+                ['-1'],
+                ['-42'],
+                [str(-(2**31 - 1))],
+                [str(-(2**31))],
+                ['-12345678']
+            ]),
+            ok(5))
+        self.assertEqual([
+            -1, -42, -(2**31 - 1), -(2**31), -12345678
+        ], values(self.read()))
+
+    @mute_logger('openerp.sql_db')
+    def test_out_of_range(self):
+        self.assertEqual(
+            self.import_(['value'], [[str(2**31)]]),
+            error(1, "integer out of range\n"))
+        # auto-rollbacks if error is in process_liness, but not during
+        # ir.model.data write. Can differentiate because former ends lines
+        # error lines with "!"
+        self.cr.rollback()
+        self.assertEqual(
+            self.import_(['value'], [[str(-2**32)]]),
+            error(1, "integer out of range\n"))
+
+
+    def test_nonsense(self):
+        self.assertEqual(
+            self.import_(['value'], [['zorglub']]),
+            error(1, u"'zorglub' does not seem to be an integer for field 'unknown'"))
+
+class test_float_field(ImporterCase):
+    model_name = 'export.float'
+    def test_none(self):
+        self.assertEqual(
+            self.import_(['value'], []),
+            ok(0))
+
+    def test_empty(self):
+        self.assertEqual(
+            self.import_(['value'], [['']]),
+            ok(1))
+        self.assertEqual(
+            [False],
+            values(self.read()))
+
+    def test_zero(self):
+        self.assertEqual(
+            self.import_(['value'], [['0']]),
+            ok(1))
+        self.assertEqual(
+            self.import_(['value'], [['-0']]),
+            ok(1))
+        self.assertEqual([False, False], values(self.read()))
+
+    def test_positives(self):
+        self.assertEqual(
+            self.import_(['value'], [
+                ['1'],
+                ['42'],
+                [str(2**31-1)],
+                ['12345678'],
+                [str(2**33)],
+                ['0.000001'],
+            ]),
+            ok(6))
+        self.assertEqual([
+            1, 42, 2**31-1, 12345678, 2.0**33, .000001
+        ], values(self.read()))
+
+    def test_negatives(self):
+        self.assertEqual(
+            self.import_(['value'], [
+                ['-1'],
+                ['-42'],
+                [str(-2**31 + 1)],
+                [str(-2**31)],
+                ['-12345678'],
+                [str(-2**33)],
+                ['-0.000001'],
+            ]),
+            ok(7))
+        self.assertEqual([
+            -1, -42, -(2**31 - 1), -(2**31), -12345678, -2.0**33, -.000001
+        ], values(self.read()))
+
+    def test_nonsense(self):
+        self.assertEqual(
+            self.import_(['value'], [['foobar']]),
+            error(1, u"'foobar' does not seem to be a number for field 'unknown'"))
+
+class test_string_field(ImporterCase):
+    model_name = 'export.string.bounded'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.import_(['value'], [['']]),
+            ok(1))
+        self.assertEqual([False], values(self.read()))
+
+    def test_imported(self):
+        self.assertEqual(
+            self.import_(['value'], [
+                [u'foobar'],
+                [u'foobarbaz'],
+                [u'Með suð í eyrum við spilum endalaust'],
+                [u"People 'get' types. They use them all the time. Telling "
+                 u"someone he can't pound a nail with a banana doesn't much "
+                 u"surprise him."]
+            ]),
+            ok(4))
+        self.assertEqual([
+            u"foobar",
+            u"foobarbaz",
+            u"Með suð í eyrum ",
+            u"People 'get' typ",
+        ], values(self.read()))
+
+class test_unbound_string_field(ImporterCase):
+    model_name = 'export.string'
+
+    def test_imported(self):
+        self.assertEqual(
+            self.import_(['value'], [
+                [u'í dag viðrar vel til loftárása'],
+                # ackbar.jpg
+                [u"If they ask you about fun, you tell them – fun is a filthy"
+                 u" parasite"]
+            ]),
+            ok(2))
+        self.assertEqual([
+            u"í dag viðrar vel til loftárása",
+            u"If they ask you about fun, you tell them – fun is a filthy parasite"
+        ], values(self.read()))
+
+class test_text(ImporterCase):
+    model_name = 'export.text'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.import_(['value'], [['']]),
+            ok(1))
+        self.assertEqual([False], values(self.read()))
+
+    def test_imported(self):
+        s = (u"Breiðskífa er notað um útgefna hljómplötu sem inniheldur "
+             u"stúdíóupptökur frá einum flytjanda. Breiðskífur eru oftast "
+             u"milli 25-80 mínútur og er lengd þeirra oft miðuð við 33⅓ "
+             u"snúninga 12 tommu vínylplötur (sem geta verið allt að 30 mín "
+             u"hvor hlið).\n\nBreiðskífur eru stundum tvöfaldar og eru þær þá"
+             u" gefnar út á tveimur geisladiskum eða tveimur vínylplötum.")
+        self.assertEqual(
+            self.import_(['value'], [[s]]),
+            ok(1))
+        self.assertEqual([s], values(self.read()))
+
+class test_selection(ImporterCase):
+    model_name = 'export.selection'
+    translations_fr = [
+        ("Qux", "toto"),
+        ("Bar", "titi"),
+        ("Foo", "tete"),
+    ]
+
+    def test_imported(self):
+        self.assertEqual(
+            self.import_(['value'], [
+                ['Qux'],
+                ['Bar'],
+                ['Foo'],
+                ['2'],
+            ]),
+            ok(4))
+        self.assertEqual([3, 2, 1, 2], values(self.read()))
+
+    def test_imported_translated(self):
+        self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, {
+            'name': u'Français',
+            'code': 'fr_FR',
+            'translatable': True,
+            'date_format': '%d.%m.%Y',
+            'decimal_point': ',',
+            'thousands_sep': ' ',
+        })
+        Translations = self.registry('ir.translation')
+        for source, value in self.translations_fr:
+            Translations.create(self.cr, openerp.SUPERUSER_ID, {
+                'name': 'export.selection,value',
+                'lang': 'fr_FR',
+                'type': 'selection',
+                'src': source,
+                'value': value
+            })
+
+        self.assertEqual(
+            self.import_(['value'], [
+                ['toto'],
+                ['tete'],
+                ['titi'],
+            ], context={'lang': 'fr_FR'}),
+            ok(3))
+        self.assertEqual([3, 1, 2], values(self.read()))
+        self.assertEqual(
+            self.import_(['value'], [['Foo']], context={'lang': 'fr_FR'}),
+            ok(1))
+
+    def test_invalid(self):
+        self.assertEqual(
+            self.import_(['value'], [['Baz']]),
+            error(1, u"Value 'Baz' not found in selection field 'unknown'"))
+        self.cr.rollback()
+        self.assertEqual(
+            self.import_(['value'], [[42]]),
+            error(1, u"Value '42' not found in selection field 'unknown'"))
+
+class test_selection_function(ImporterCase):
+    model_name = 'export.selection.function'
+    translations_fr = [
+        ("Corge", "toto"),
+        ("Grault", "titi"),
+        ("Wheee", "tete"),
+        ("Moog", "tutu"),
+    ]
+
+    def test_imported(self):
+        """ import uses fields_get, so translates import label (may or may not
+        be good news) *and* serializes the selection function to reverse it:
+        import does not actually know that the selection field uses a function
+        """
+        # NOTE: conflict between a value and a label => ?
+        self.assertEqual(
+            self.import_(['value'], [
+                ['3'],
+                ["Grault"],
+            ]),
+            ok(2))
+        self.assertEqual(
+            ['3', '1'],
+            values(self.read()))
+
+    def test_translated(self):
+        """ Expects output of selection function returns translated labels
+        """
+        self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, {
+            'name': u'Français',
+            'code': 'fr_FR',
+            'translatable': True,
+            'date_format': '%d.%m.%Y',
+            'decimal_point': ',',
+            'thousands_sep': ' ',
+        })
+        Translations = self.registry('ir.translation')
+        for source, value in self.translations_fr:
+            Translations.create(self.cr, openerp.SUPERUSER_ID, {
+                'name': 'export.selection,value',
+                'lang': 'fr_FR',
+                'type': 'selection',
+                'src': source,
+                'value': value
+            })
+        self.assertEqual(
+            self.import_(['value'], [
+                ['toto'],
+                ['tete'],
+            ], context={'lang': 'fr_FR'}),
+            ok(2))
+        self.assertEqual(
+            self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'}),
+            ok(1))
+
+class test_m2o(ImporterCase):
+    model_name = 'export.many2one'
+
+    def test_by_name(self):
+        # create integer objects
+        integer_id1 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        integer_id2 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 36})
+        # get its name
+        name1 = dict(self.registry('export.integer').name_get(
+            self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1]
+        name2 = dict(self.registry('export.integer').name_get(
+            self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2]
+
+        self.assertEqual(
+            self.import_(['value'], [
+                # import by name_get
+                [name1],
+                [name1],
+                [name2],
+            ]),
+            ok(3))
+        # correct ids assigned to corresponding records
+        self.assertEqual([
+            (integer_id1, name1),
+            (integer_id1, name1),
+            (integer_id2, name2),],
+            values(self.read()))
+
+    def test_by_xid(self):
+        ExportInteger = self.registry('export.integer')
+        integer_id = ExportInteger.create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        xid = self.xid(ExportInteger.browse(
+            self.cr, openerp.SUPERUSER_ID, [integer_id])[0])
+
+        self.assertEqual(
+            self.import_(['value/id'], [[xid]]),
+            ok(1))
+        b = self.browse()
+        self.assertEqual(42, b[0].value.value)
+
+    def test_by_id(self):
+        integer_id = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        self.assertEqual(
+            self.import_(['value/.id'], [[integer_id]]),
+            ok(1))
+        b = self.browse()
+        self.assertEqual(42, b[0].value.value)
+
+    def test_by_names(self):
+        integer_id1 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        integer_id2 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        name1 = dict(self.registry('export.integer').name_get(
+            self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1]
+        name2 = dict(self.registry('export.integer').name_get(
+            self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2]
+        # names should be the same
+        self.assertEqual(name1, name2)
+
+        self.assertEqual(
+            self.import_(['value'], [[name2]]),
+            ok(1))
+        self.assertEqual([
+            (integer_id1, name1)
+        ], values(self.read()))
+
+    def test_fail_by_implicit_id(self):
+        """ Can't implicitly import records by id
+        """
+        # create integer objects
+        integer_id1 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        integer_id2 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 36})
+
+        self.assertEqual(
+            self.import_(['value'], [
+                # import by id, without specifying it
+                [integer_id1],
+                [integer_id2],
+                [integer_id1],
+            ]),
+            error(1, u"No matching record found for name '%s' in field 'unknown'" % integer_id1))
+
+    def test_sub_field(self):
+        """ Does not implicitly create the record, does not warn that you can't
+        import m2o subfields (at all)...
+        """
+        self.assertEqual(
+            self.import_(['value/value'], [['42']]),
+            error(1, u"Can not create Many-To-One records indirectly, import the field separately"))
+
+    def test_fail_noids(self):
+        self.assertEqual(
+            self.import_(['value'], [['nameisnoexist:3']]),
+            error(1, u"No matching record found for name 'nameisnoexist:3' in field 'unknown'"))
+        self.cr.rollback()
+        self.assertEqual(
+            self.import_(['value/id'], [['noxidhere']]),
+            error(1, u"No matching record found for external id 'noxidhere' in field 'unknown'"))
+        self.cr.rollback()
+        self.assertEqual(
+            self.import_(['value/.id'], [[66]]),
+            error(1, u"No matching record found for database id '66' in field 'unknown'"))
+
+class test_m2m(ImporterCase):
+    model_name = 'export.many2many'
+
+    # apparently, one and only thing which works is a
+    # csv_internal_sep-separated list of ids, xids, or names (depending if
+    # m2m/.id, m2m/id or m2m[/anythingelse]
+    def test_ids(self):
+        id1 = self.registry('export.many2many.other').create(
+                self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
+        id2 = self.registry('export.many2many.other').create(
+                self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
+        id3 = self.registry('export.many2many.other').create(
+                self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
+        id4 = self.registry('export.many2many.other').create(
+                self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
+        id5 = self.registry('export.many2many.other').create(
+                self.cr, openerp.SUPERUSER_ID, {'value': 99, 'str': 'record4'})
+
+        self.assertEqual(
+            self.import_(['value/.id'], [
+                ['%d,%d' % (id1, id2)],
+                ['%d,%d,%d' % (id1, id3, id4)],
+                ['%d,%d,%d' % (id1, id2, id3)],
+                ['%d' % id5]
+            ]),
+            ok(4))
+        ids = lambda records: [record.id for record in records]
+
+        b = self.browse()
+        self.assertEqual(ids(b[0].value), [id1, id2])
+        self.assertEqual(values(b[0].value), [3, 44])
+
+        self.assertEqual(ids(b[2].value), [id1, id2, id3])
+        self.assertEqual(values(b[2].value), [3, 44, 84])
+
+    def test_noids(self):
+        self.assertEqual(
+            self.import_(['value/.id'], [['42']]),
+            error(1, u"No matching record found for database id '42' in field 'unknown'"))
+
+    def test_xids(self):
+        M2O_o = self.registry('export.many2many.other')
+        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
+        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
+        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
+        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
+        records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4])
+
+        self.assertEqual(
+            self.import_(['value/id'], [
+                ['%s,%s' % (self.xid(records[0]), self.xid(records[1]))],
+                ['%s' % self.xid(records[3])],
+                ['%s,%s' % (self.xid(records[2]), self.xid(records[1]))],
+            ]),
+            ok(3))
+
+        b = self.browse()
+        self.assertEqual(values(b[0].value), [3, 44])
+        self.assertEqual(values(b[2].value), [44, 84])
+    def test_noxids(self):
+        self.assertEqual(
+            self.import_(['value/id'], [['noxidforthat']]),
+            error(1, u"No matching record found for external id 'noxidforthat' in field 'unknown'"))
+
+    def test_names(self):
+        M2O_o = self.registry('export.many2many.other')
+        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
+        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
+        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
+        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
+        records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4])
+
+        name = lambda record: dict(record.name_get())[record.id]
+
+        self.assertEqual(
+            self.import_(['value'], [
+                ['%s,%s' % (name(records[1]), name(records[2]))],
+                ['%s,%s,%s' % (name(records[0]), name(records[1]), name(records[2]))],
+                ['%s,%s' % (name(records[0]), name(records[3]))],
+            ]),
+            ok(3))
+
+        b = self.browse()
+        self.assertEqual(values(b[1].value), [3, 44, 84])
+        self.assertEqual(values(b[2].value), [3, 9])
+
+    def test_nonames(self):
+        self.assertEqual(
+            self.import_(['value'], [['wherethem2mhavenonames']]),
+            error(1, u"No matching record found for name 'wherethem2mhavenonames' in field 'unknown'"))
+
+    def test_import_to_existing(self):
+        M2O_o = self.registry('export.many2many.other')
+        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
+        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
+        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
+        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
+
+        xid = 'myxid'
+        self.assertEqual(
+            self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id1, id2)]]),
+            ok(1))
+        self.assertEqual(
+            self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id3, id4)]]),
+            ok(1))
+
+        b = self.browse()
+        self.assertEqual(len(b), 1)
+        # TODO: replacement of existing m2m values is correct?
+        self.assertEqual(values(b[0].value), [84, 9])
+
+class test_o2m(ImporterCase):
+    model_name = 'export.one2many'
+
+    def test_name_get(self):
+        s = u'Java is a DSL for taking large XML files and converting them to' \
+            u' stack traces'
+        self.assertEqual(
+            self.import_(
+                ['const', 'value'],
+                [['5', s]]),
+            error(1, u"No matching record found for name '%s' in field 'unknown'" % s))
+
+    def test_single(self):
+        self.assertEqual(
+            self.import_(['const', 'value/value'], [
+                ['5', '63']
+            ]),
+            ok(1))
+
+        (b,) = self.browse()
+        self.assertEqual(b.const, 5)
+        self.assertEqual(values(b.value), [63])
+
+    def test_multicore(self):
+        self.assertEqual(
+            self.import_(['const', 'value/value'], [
+                ['5', '63'],
+                ['6', '64'],
+            ]),
+            ok(2))
+
+        b1, b2 = self.browse()
+        self.assertEqual(b1.const, 5)
+        self.assertEqual(values(b1.value), [63])
+        self.assertEqual(b2.const, 6)
+        self.assertEqual(values(b2.value), [64])
+
+    def test_multisub(self):
+        self.assertEqual(
+            self.import_(['const', 'value/value'], [
+                ['5', '63'],
+                ['', '64'],
+                ['', '65'],
+                ['', '66'],
+            ]),
+            ok(4))
+
+        (b,) = self.browse()
+        self.assertEqual(values(b.value), [63, 64, 65, 66])
+
+    def test_multi_subfields(self):
+        self.assertEqual(
+            self.import_(['value/str', 'const', 'value/value'], [
+                ['this', '5', '63'],
+                ['is', '', '64'],
+                ['the', '', '65'],
+                ['rhythm', '', '66'],
+            ]),
+            ok(4))
+
+        (b,) = self.browse()
+        self.assertEqual(values(b.value), [63, 64, 65, 66])
+        self.assertEqual(
+            values(b.value, 'str'),
+            'this is the rhythm'.split())
+
+    def test_link_inline(self):
+        id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Bf', 'value': 109
+        })
+        id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Me', 'value': 262
+        })
+
+        try:
+            self.import_(['const', 'value/.id'], [
+                ['42', '%d,%d' % (id1, id2)]
+            ])
+        except ValueError, e:
+            # should be Exception(Database ID doesn't exist: export.one2many.child : $id1,$id2)
+            self.assertIs(type(e), ValueError)
+            self.assertEqual(
+                e.args[0],
+                "invalid literal for int() with base 10: '%d,%d'" % (id1, id2))
+
+    def test_link(self):
+        id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Bf', 'value': 109
+        })
+        id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Me', 'value': 262
+        })
+
+        self.assertEqual(
+            self.import_(['const', 'value/.id'], [
+                ['42', str(id1)],
+                ['', str(id2)],
+            ]),
+            ok(2))
+
+        [b] = self.browse()
+        self.assertEqual(b.const, 42)
+        # automatically forces link between core record and o2ms
+        self.assertEqual(values(b.value), [109, 262])
+        self.assertEqual(values(b.value, field='parent_id'), [b, b])
+
+    def test_link_2(self):
+        O2M_c = self.registry('export.one2many.child')
+        id1 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Bf', 'value': 109
+        })
+        id2 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Me', 'value': 262
+        })
+
+        self.assertEqual(
+            self.import_(['const', 'value/.id', 'value/value'], [
+                ['42', str(id1), '1'],
+                ['', str(id2), '2'],
+            ]),
+            ok(2))
+
+        [b] = self.browse()
+        self.assertEqual(b.const, 42)
+        self.assertEqual(values(b.value), [1, 2])
+        self.assertEqual(values(b.value, field='parent_id'), [b, b])
+
+class test_o2m_multiple(ImporterCase):
+    model_name = 'export.one2many.multiple'
+
+    def test_multi_mixed(self):
+        self.assertEqual(
+            self.import_(['const', 'child1/value', 'child2/value'], [
+                ['5', '11', '21'],
+                ['', '12', '22'],
+                ['', '13', '23'],
+                ['', '14', ''],
+            ]),
+            ok(4))
+
+        [b] = self.browse()
+        self.assertEqual(values(b.child1), [11, 12, 13, 14])
+        self.assertEqual(values(b.child2), [21, 22, 23])
+
+    def test_multi(self):
+        self.assertEqual(
+            self.import_(['const', 'child1/value', 'child2/value'], [
+                ['5', '11', '21'],
+                ['', '12', ''],
+                ['', '13', ''],
+                ['', '14', ''],
+                ['', '', '22'],
+                ['', '', '23'],
+            ]),
+            ok(6))
+
+        [b] = self.browse()
+        self.assertEqual(values(b.child1), [11, 12, 13, 14])
+        self.assertEqual(values(b.child2), [21, 22, 23])
+
+    def test_multi_fullsplit(self):
+        self.assertEqual(
+            self.import_(['const', 'child1/value', 'child2/value'], [
+                ['5', '11', ''],
+                ['', '12', ''],
+                ['', '13', ''],
+                ['', '14', ''],
+                ['', '', '21'],
+                ['', '', '22'],
+                ['', '', '23'],
+            ]),
+            ok(7))
+
+        [b] = self.browse()
+        self.assertEqual(b.const, 5)
+        self.assertEqual(values(b.child1), [11, 12, 13, 14])
+        self.assertEqual(values(b.child2), [21, 22, 23])
+
+# function, related, reference: written to db as-is...
+# => function uses @type for value coercion/conversion
diff --git a/openerp/addons/test_impex/tests/test_load.py b/openerp/addons/test_impex/tests/test_load.py
new file mode 100644 (file)
index 0000000..2604922
--- /dev/null
@@ -0,0 +1,1173 @@
+# -*- coding: utf-8 -*-
+import json
+import pkgutil
+import unittest2
+
+import openerp.modules.registry
+import openerp
+
+from openerp.tests import common
+from openerp.tools.misc import mute_logger
+
+def message(msg, type='error', from_=0, to_=0, record=0, field='value', **kwargs):
+    return dict(kwargs,
+                type=type, rows={'from': from_, 'to': to_}, record=record,
+                field=field, message=msg)
+def moreaction(**kwargs):
+    return dict(kwargs,
+        type='ir.actions.act_window',
+        target='new',
+        view_mode='tree,form',
+        view_type='form',
+        views=[(False, 'tree'), (False, 'form')],
+        help=u"See all possible values")
+
+def values(seq, field='value'):
+    return [item[field] for item in seq]
+
+class ImporterCase(common.TransactionCase):
+    model_name = False
+
+    def __init__(self, *args, **kwargs):
+        super(ImporterCase, self).__init__(*args, **kwargs)
+        self.model = None
+
+    def setUp(self):
+        super(ImporterCase, self).setUp()
+        self.model = self.registry(self.model_name)
+        self.registry('ir.model.data').clear_caches()
+
+    def import_(self, fields, rows, context=None):
+        return self.model.load(
+            self.cr, openerp.SUPERUSER_ID, fields, rows, context=context)
+    def read(self, fields=('value',), domain=(), context=None):
+        return self.model.read(
+            self.cr, openerp.SUPERUSER_ID,
+            self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context),
+            fields=fields, context=context)
+    def browse(self, domain=(), context=None):
+        return self.model.browse(
+            self.cr, openerp.SUPERUSER_ID,
+            self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context),
+            context=context)
+
+    def xid(self, record):
+        ModelData = self.registry('ir.model.data')
+
+        ids = ModelData.search(
+            self.cr, openerp.SUPERUSER_ID,
+            [('model', '=', record._table_name), ('res_id', '=', record.id)])
+        if ids:
+            d = ModelData.read(
+                self.cr, openerp.SUPERUSER_ID, ids, ['name', 'module'])[0]
+            if d['module']:
+                return '%s.%s' % (d['module'], d['name'])
+            return d['name']
+
+        name = dict(record.name_get())[record.id]
+        # fix dotted name_get results, otherwise xid lookups blow up
+        name = name.replace('.', '-')
+        ModelData.create(self.cr, openerp.SUPERUSER_ID, {
+            'name': name,
+            'model': record._table_name,
+            'res_id': record.id,
+            'module': '__test__'
+        })
+        return '__test__.' + name
+
+    def add_translations(self, name, type, code, *tnx):
+        Lang = self.registry('res.lang')
+        if not Lang.search(self.cr, openerp.SUPERUSER_ID, [('code', '=', code)]):
+            Lang.create(self.cr, openerp.SUPERUSER_ID, {
+                'name': code,
+                'code': code,
+                'translatable': True,
+                'date_format': '%d.%m.%Y',
+                'decimal_point': ',',
+            })
+        Translations = self.registry('ir.translation')
+        for source, value in tnx:
+            Translations.create(self.cr, openerp.SUPERUSER_ID, {
+                'name': name,
+                'lang': code,
+                'type': type,
+                'src': source,
+                'value': value,
+                'state': 'translated',
+            })
+
+class test_ids_stuff(ImporterCase):
+    model_name = 'export.integer'
+
+    def test_create_with_id(self):
+        result = self.import_(['.id', 'value'], [['42', '36']])
+        self.assertIs(result['ids'], False)
+        self.assertEqual(result['messages'], [{
+            'type': 'error',
+            'rows': {'from': 0, 'to': 0},
+            'record': 0,
+            'field': '.id',
+            'message': u"Unknown database identifier '42'",
+        }])
+    def test_create_with_xid(self):
+        result = self.import_(['id', 'value'], [['somexmlid', '42']])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+        self.assertEqual(
+            'somexmlid',
+            self.xid(self.browse()[0]))
+
+    def test_update_with_id(self):
+        id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': 36})
+        self.assertEqual(
+            36,
+            self.model.browse(self.cr, openerp.SUPERUSER_ID, id).value)
+
+        result = self.import_(['.id', 'value'], [[str(id), '42']])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+        self.assertEqual(
+            [42], # updated value to imported
+            values(self.read()))
+
+    def test_update_with_xid(self):
+        self.import_(['id', 'value'], [['somexmlid', '36']])
+        self.assertEqual([36], values(self.read()))
+
+        self.import_(['id', 'value'], [['somexmlid', '1234567']])
+        self.assertEqual([1234567], values(self.read()))
+
+class test_boolean_field(ImporterCase):
+    model_name = 'export.boolean'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.import_(['value'], []),
+            {'ids': [], 'messages': []})
+
+    def test_exported(self):
+        result = self.import_(['value'], [['False'], ['True'], ])
+        self.assertEqual(len(result['ids']), 2)
+        self.assertFalse(result['messages'])
+        records = self.read()
+        self.assertEqual([
+            False,
+            True,
+        ], values(records))
+
+    def test_falses(self):
+        for lang, source, value in [('fr_FR', 'no', u'non'),
+                                    ('de_DE', 'no', u'nein'),
+                                    ('ru_RU', 'no', u'нет'),
+                                    ('nl_BE', 'false', u'vals'),
+                                    ('lt_LT', 'false', u'klaidingas')]:
+            self.add_translations('test_import.py', 'code', lang, (source, value))
+        falses = [[u'0'], [u'no'], [u'false'], [u'FALSE'], [u''],
+                  [u'non'], # no, fr
+                  [u'nein'], # no, de
+                  [u'нет'], # no, ru
+                  [u'vals'], # false, nl
+                  [u'klaidingas'], # false, lt,
+        ]
+
+        result = self.import_(['value'], falses)
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), len(falses))
+        self.assertEqual([False] * len(falses), values(self.read()))
+
+    def test_trues(self):
+        trues = [['None'], ['nil'], ['()'], ['f'], ['#f'],
+                  # Problem: OpenOffice (and probably excel) output localized booleans
+                  ['VRAI'], ['ok'], ['true'], ['yes'], ['1'], ]
+        result = self.import_(['value'], trues)
+        self.assertEqual(len(result['ids']), 10)
+        self.assertEqual(result['messages'], [
+            message(u"Unknown value '%s' for boolean field 'unknown', assuming 'yes'" % v[0],
+                    moreinfo=u"Use '1' for yes and '0' for no",
+                    type='warning', from_=i, to_=i, record=i)
+            for i, v in enumerate(trues)
+            if v[0] not in ('true', 'yes', '1')
+        ])
+        self.assertEqual(
+            [True] * 10,
+            values(self.read()))
+
+class test_integer_field(ImporterCase):
+    model_name = 'export.integer'
+
+    def test_none(self):
+        self.assertEqual(
+            self.import_(['value'], []),
+            {'ids': [], 'messages': []})
+
+    def test_empty(self):
+        result = self.import_(['value'], [['']])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+        self.assertEqual(
+            [False],
+            values(self.read()))
+
+    def test_zero(self):
+        result = self.import_(['value'], [['0']])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+
+        result = self.import_(['value'], [['-0']])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+
+        self.assertEqual([False, False], values(self.read()))
+
+    def test_positives(self):
+        result = self.import_(['value'], [
+            ['1'],
+            ['42'],
+            [str(2**31-1)],
+            ['12345678']
+        ])
+        self.assertEqual(len(result['ids']), 4)
+        self.assertFalse(result['messages'])
+
+        self.assertEqual([
+            1, 42, 2**31-1, 12345678
+        ], values(self.read()))
+
+    def test_negatives(self):
+        result = self.import_(['value'], [
+            ['-1'],
+            ['-42'],
+            [str(-(2**31 - 1))],
+            [str(-(2**31))],
+            ['-12345678']
+        ])
+        self.assertEqual(len(result['ids']), 5)
+        self.assertFalse(result['messages'])
+        self.assertEqual([
+            -1, -42, -(2**31 - 1), -(2**31), -12345678
+        ], values(self.read()))
+
+    @mute_logger('openerp.sql_db', 'openerp.osv.orm')
+    def test_out_of_range(self):
+        result = self.import_(['value'], [[str(2**31)]])
+        self.assertIs(result['ids'], False)
+        self.assertEqual(result['messages'], [{
+            'type': 'error',
+            'rows': {'from': 0, 'to': 0},
+            'record': 0,
+            'message': "integer out of range\n"
+        }])
+
+        result = self.import_(['value'], [[str(-2**32)]])
+        self.assertIs(result['ids'], False)
+        self.assertEqual(result['messages'], [{
+            'type': 'error',
+            'rows': {'from': 0, 'to': 0},
+            'record': 0,
+            'message': "integer out of range\n"
+        }])
+
+    def test_nonsense(self):
+        result = self.import_(['value'], [['zorglub']])
+        self.assertIs(result['ids'], False)
+        self.assertEqual(result['messages'], [{
+            'type': 'error',
+            'rows': {'from': 0, 'to': 0},
+            'record': 0,
+            'field': 'value',
+            'message': u"'zorglub' does not seem to be an integer for field 'unknown'",
+        }])
+
+class test_float_field(ImporterCase):
+    model_name = 'export.float'
+    def test_none(self):
+        self.assertEqual(
+            self.import_(['value'], []),
+            {'ids': [], 'messages': []})
+
+    def test_empty(self):
+        result = self.import_(['value'], [['']])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+        self.assertEqual(
+            [False],
+            values(self.read()))
+
+    def test_zero(self):
+        result = self.import_(['value'], [['0']])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+
+        result = self.import_(['value'], [['-0']])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+
+        self.assertEqual([False, False], values(self.read()))
+
+    def test_positives(self):
+        result = self.import_(['value'], [
+            ['1'],
+            ['42'],
+            [str(2**31-1)],
+            ['12345678'],
+            [str(2**33)],
+            ['0.000001'],
+        ])
+        self.assertEqual(len(result['ids']), 6)
+        self.assertFalse(result['messages'])
+
+        self.assertEqual([
+            1, 42, 2**31-1, 12345678, 2.0**33, .000001
+        ], values(self.read()))
+
+    def test_negatives(self):
+        result = self.import_(['value'], [
+            ['-1'],
+            ['-42'],
+            [str(-2**31 + 1)],
+            [str(-2**31)],
+            ['-12345678'],
+            [str(-2**33)],
+            ['-0.000001'],
+        ])
+        self.assertEqual(len(result['ids']), 7)
+        self.assertFalse(result['messages'])
+        self.assertEqual([
+            -1, -42, -(2**31 - 1), -(2**31), -12345678, -2.0**33, -.000001
+        ], values(self.read()))
+
+    def test_nonsense(self):
+        result = self.import_(['value'], [['foobar']])
+        self.assertIs(result['ids'], False)
+        self.assertEqual(result['messages'], [
+            message(u"'foobar' does not seem to be a number for field 'unknown'")])
+
+class test_string_field(ImporterCase):
+    model_name = 'export.string.bounded'
+
+    def test_empty(self):
+        result = self.import_(['value'], [['']])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+        self.assertEqual([False], values(self.read()))
+
+    def test_imported(self):
+        result = self.import_(['value'], [
+            [u'foobar'],
+            [u'foobarbaz'],
+            [u'Með suð í eyrum við spilum endalaust'],
+            [u"People 'get' types. They use them all the time. Telling "
+             u"someone he can't pound a nail with a banana doesn't much "
+             u"surprise him."]
+        ])
+        self.assertEqual(len(result['ids']), 4)
+        self.assertFalse(result['messages'])
+        self.assertEqual([
+            u"foobar",
+            u"foobarbaz",
+            u"Með suð í eyrum ",
+            u"People 'get' typ",
+        ], values(self.read()))
+
+class test_unbound_string_field(ImporterCase):
+    model_name = 'export.string'
+
+    def test_imported(self):
+        result = self.import_(['value'], [
+            [u'í dag viðrar vel til loftárása'],
+            # ackbar.jpg
+            [u"If they ask you about fun, you tell them – fun is a filthy"
+             u" parasite"]
+        ])
+        self.assertEqual(len(result['ids']), 2)
+        self.assertFalse(result['messages'])
+        self.assertEqual([
+            u"í dag viðrar vel til loftárása",
+            u"If they ask you about fun, you tell them – fun is a filthy parasite"
+        ], values(self.read()))
+
+class test_required_string_field(ImporterCase):
+    model_name = 'export.string.required'
+
+    @mute_logger('openerp.sql_db', 'openerp.osv.orm')
+    def test_empty(self):
+        result = self.import_(['value'], [[]])
+        self.assertEqual(result['messages'], [message(
+            u"Missing required value for the field 'unknown' (value)")])
+        self.assertIs(result['ids'], False)
+
+    @mute_logger('openerp.sql_db', 'openerp.osv.orm')
+    def test_not_provided(self):
+        result = self.import_(['const'], [['12']])
+        self.assertEqual(result['messages'], [message(
+            u"Missing required value for the field 'unknown' (value)")])
+        self.assertIs(result['ids'], False)
+
+class test_text(ImporterCase):
+    model_name = 'export.text'
+
+    def test_empty(self):
+        result = self.import_(['value'], [['']])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+        self.assertEqual([False], values(self.read()))
+
+    def test_imported(self):
+        s = (u"Breiðskífa er notað um útgefna hljómplötu sem inniheldur "
+             u"stúdíóupptökur frá einum flytjanda. Breiðskífur eru oftast "
+             u"milli 25-80 mínútur og er lengd þeirra oft miðuð við 33⅓ "
+             u"snúninga 12 tommu vínylplötur (sem geta verið allt að 30 mín "
+             u"hvor hlið).\n\nBreiðskífur eru stundum tvöfaldar og eru þær þá"
+             u" gefnar út á tveimur geisladiskum eða tveimur vínylplötum.")
+        result = self.import_(['value'], [[s]])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+        self.assertEqual([s], values(self.read()))
+
+class test_selection(ImporterCase):
+    model_name = 'export.selection'
+    translations_fr = [
+        ("Foo", "tete"),
+        ("Bar", "titi"),
+        ("Qux", "toto"),
+    ]
+
+    def test_imported(self):
+        result = self.import_(['value'], [
+            ['Qux'],
+            ['Bar'],
+            ['Foo'],
+            ['2'],
+        ])
+        self.assertEqual(len(result['ids']), 4)
+        self.assertFalse(result['messages'])
+        self.assertEqual([3, 2, 1, 2], values(self.read()))
+
+    def test_imported_translated(self):
+        self.add_translations(
+            'export.selection,value', 'selection', 'fr_FR', *self.translations_fr)
+
+        result = self.import_(['value'], [
+            ['toto'],
+            ['tete'],
+            ['titi'],
+        ], context={'lang': 'fr_FR'})
+        self.assertEqual(len(result['ids']), 3)
+        self.assertFalse(result['messages'])
+
+        self.assertEqual([3, 1, 2], values(self.read()))
+
+        result = self.import_(['value'], [['Foo']], context={'lang': 'fr_FR'})
+        self.assertEqual(len(result['ids']), 1)
+        self.assertFalse(result['messages'])
+
+    def test_invalid(self):
+        result = self.import_(['value'], [['Baz']])
+        self.assertIs(result['ids'], False)
+        self.assertEqual(result['messages'], [message(
+            u"Value 'Baz' not found in selection field 'unknown'",
+            moreinfo="Foo Bar Qux 4".split())])
+
+        result = self.import_(['value'], [[42]])
+        self.assertIs(result['ids'], False)
+        self.assertEqual(result['messages'], [message(
+            u"Value '42' not found in selection field 'unknown'",
+            moreinfo="Foo Bar Qux 4".split())])
+
+class test_selection_with_default(ImporterCase):
+    model_name = 'export.selection.withdefault'
+
+    def test_empty(self):
+        """ Empty cells should set corresponding field to False
+        """
+        result = self.import_(['value'], [['']])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        self.assertEqual(
+            values(self.read()),
+            [False])
+
+    def test_default(self):
+        """ Non-provided cells should set corresponding field to default
+        """
+        result = self.import_(['const'], [['42']])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        self.assertEqual(
+            values(self.read()),
+            [2])
+
+class test_selection_function(ImporterCase):
+    model_name = 'export.selection.function'
+    translations_fr = [
+        ("Corge", "toto"),
+        ("Grault", "titi"),
+        ("Wheee", "tete"),
+        ("Moog", "tutu"),
+    ]
+
+    def test_imported(self):
+        """ import uses fields_get, so translates import label (may or may not
+        be good news) *and* serializes the selection function to reverse it:
+        import does not actually know that the selection field uses a function
+        """
+        # NOTE: conflict between a value and a label => pick first
+        result = self.import_(['value'], [
+            ['3'],
+            ["Grault"],
+        ])
+        self.assertEqual(len(result['ids']), 2)
+        self.assertFalse(result['messages'])
+        self.assertEqual(
+            ['3', '1'],
+            values(self.read()))
+
+    def test_translated(self):
+        """ Expects output of selection function returns translated labels
+        """
+        self.add_translations(
+            'export.selection,value', 'selection', 'fr_FR', *self.translations_fr)
+
+        result = self.import_(['value'], [
+            ['titi'],
+            ['tete'],
+        ], context={'lang': 'fr_FR'})
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 2)
+        self.assertEqual(values(self.read()), ['1', '2'])
+
+        result = self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'})
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+class test_m2o(ImporterCase):
+    model_name = 'export.many2one'
+
+    def test_by_name(self):
+        # create integer objects
+        integer_id1 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        integer_id2 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 36})
+        # get its name
+        name1 = dict(self.registry('export.integer').name_get(
+            self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1]
+        name2 = dict(self.registry('export.integer').name_get(
+            self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2]
+
+        result = self.import_(['value'], [
+            # import by name_get
+            [name1],
+            [name1],
+            [name2],
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 3)
+        # correct ids assigned to corresponding records
+        self.assertEqual([
+            (integer_id1, name1),
+            (integer_id1, name1),
+            (integer_id2, name2),],
+            values(self.read()))
+
+    def test_by_xid(self):
+        ExportInteger = self.registry('export.integer')
+        integer_id = ExportInteger.create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        xid = self.xid(ExportInteger.browse(
+            self.cr, openerp.SUPERUSER_ID, [integer_id])[0])
+
+        result = self.import_(['value/id'], [[xid]])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+        b = self.browse()
+        self.assertEqual(42, b[0].value.value)
+
+    def test_by_id(self):
+        integer_id = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        result = self.import_(['value/.id'], [[integer_id]])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+        b = self.browse()
+        self.assertEqual(42, b[0].value.value)
+
+    def test_by_names(self):
+        integer_id1 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        integer_id2 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        name1 = dict(self.registry('export.integer').name_get(
+            self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1]
+        name2 = dict(self.registry('export.integer').name_get(
+            self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2]
+        # names should be the same
+        self.assertEqual(name1, name2)
+
+        result = self.import_(['value'], [[name2]])
+        self.assertEqual(
+            result['messages'],
+            [message(u"Found multiple matches for field 'unknown' (2 matches)",
+                     type='warning')])
+        self.assertEqual(len(result['ids']), 1)
+        self.assertEqual([
+            (integer_id1, name1)
+        ], values(self.read()))
+
+    def test_fail_by_implicit_id(self):
+        """ Can't implicitly import records by id
+        """
+        # create integer objects
+        integer_id1 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 42})
+        integer_id2 = self.registry('export.integer').create(
+            self.cr, openerp.SUPERUSER_ID, {'value': 36})
+
+        # Because name_search all the things. Fallback schmallback
+        result = self.import_(['value'], [
+                # import by id, without specifying it
+                [integer_id1],
+                [integer_id2],
+                [integer_id1],
+        ])
+        self.assertEqual(result['messages'], [
+            message(u"No matching record found for name '%s' in field 'unknown'" % id,
+                    from_=index, to_=index, record=index,
+                    moreinfo=moreaction(res_model='export.integer'))
+            for index, id in enumerate([integer_id1, integer_id2, integer_id1])])
+        self.assertIs(result['ids'], False)
+
+    @mute_logger('openerp.sql_db')
+    def test_fail_id_mistype(self):
+        result = self.import_(['value/.id'], [["foo"]])
+
+        self.assertEqual(result['messages'], [
+            message(u"Invalid database id 'foo' for the field 'unknown'",
+                    moreinfo=moreaction(res_model='ir.model.data',
+                                        domain=[('model','=','export.integer')]))
+        ])
+        self.assertIs(result['ids'], False)
+
+    def test_sub_field(self):
+        """ Does not implicitly create the record, does not warn that you can't
+        import m2o subfields (at all)...
+        """
+        result = self.import_(['value/value'], [['42']])
+        self.assertEqual(result['messages'], [
+            message(u"Can not create Many-To-One records indirectly, import "
+                    u"the field separately")])
+        self.assertIs(result['ids'], False)
+
+    def test_fail_noids(self):
+        result = self.import_(['value'], [['nameisnoexist:3']])
+        self.assertEqual(result['messages'], [message(
+            u"No matching record found for name 'nameisnoexist:3' "
+            u"in field 'unknown'", moreinfo=moreaction(
+                res_model='export.integer'))])
+        self.assertIs(result['ids'], False)
+
+        result = self.import_(['value/id'], [['noxidhere']])
+        self.assertEqual(result['messages'], [message(
+            u"No matching record found for external id 'noxidhere' "
+            u"in field 'unknown'", moreinfo=moreaction(
+                res_model='ir.model.data', domain=[('model','=','export.integer')]))])
+        self.assertIs(result['ids'], False)
+
+        result = self.import_(['value/.id'], [['66']])
+        self.assertEqual(result['messages'], [message(
+            u"No matching record found for database id '66' "
+            u"in field 'unknown'", moreinfo=moreaction(
+                res_model='ir.model.data', domain=[('model','=','export.integer')]))])
+        self.assertIs(result['ids'], False)
+
+    def test_fail_multiple(self):
+        result = self.import_(
+            ['value', 'value/id'],
+            [['somename', 'somexid']])
+        self.assertEqual(result['messages'], [message(
+            u"Ambiguous specification for field 'unknown', only provide one of "
+            u"name, external id or database id")])
+        self.assertIs(result['ids'], False)
+
+class test_m2m(ImporterCase):
+    model_name = 'export.many2many'
+
+    # apparently, one and only thing which works is a
+    # csv_internal_sep-separated list of ids, xids, or names (depending if
+    # m2m/.id, m2m/id or m2m[/anythingelse]
+    def test_ids(self):
+        id1 = self.registry('export.many2many.other').create(
+                self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
+        id2 = self.registry('export.many2many.other').create(
+                self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
+        id3 = self.registry('export.many2many.other').create(
+                self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
+        id4 = self.registry('export.many2many.other').create(
+                self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
+        id5 = self.registry('export.many2many.other').create(
+                self.cr, openerp.SUPERUSER_ID, {'value': 99, 'str': 'record4'})
+
+        result = self.import_(['value/.id'], [
+            ['%d,%d' % (id1, id2)],
+            ['%d,%d,%d' % (id1, id3, id4)],
+            ['%d,%d,%d' % (id1, id2, id3)],
+            ['%d' % id5]
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 4)
+
+        ids = lambda records: [record.id for record in records]
+
+        b = self.browse()
+        self.assertEqual(ids(b[0].value), [id1, id2])
+        self.assertEqual(values(b[0].value), [3, 44])
+
+        self.assertEqual(ids(b[2].value), [id1, id2, id3])
+        self.assertEqual(values(b[2].value), [3, 44, 84])
+
+    def test_noids(self):
+        result = self.import_(['value/.id'], [['42']])
+        self.assertEqual(result['messages'], [message(
+            u"No matching record found for database id '42' in field "
+            u"'unknown'", moreinfo=moreaction(
+                res_model='ir.model.data', domain=[('model','=','export.many2many.other')]))])
+        self.assertIs(result['ids'], False)
+
+    def test_xids(self):
+        M2O_o = self.registry('export.many2many.other')
+        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
+        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
+        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
+        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
+        records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4])
+
+        result = self.import_(['value/id'], [
+            ['%s,%s' % (self.xid(records[0]), self.xid(records[1]))],
+            ['%s' % self.xid(records[3])],
+            ['%s,%s' % (self.xid(records[2]), self.xid(records[1]))],
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 3)
+
+        b = self.browse()
+        self.assertEqual(values(b[0].value), [3, 44])
+        self.assertEqual(values(b[2].value), [44, 84])
+    def test_noxids(self):
+        result = self.import_(['value/id'], [['noxidforthat']])
+        self.assertEqual(result['messages'], [message(
+            u"No matching record found for external id 'noxidforthat' in field"
+            u" 'unknown'", moreinfo=moreaction(
+                res_model='ir.model.data', domain=[('model','=','export.many2many.other')]))])
+        self.assertIs(result['ids'], False)
+
+    def test_names(self):
+        M2O_o = self.registry('export.many2many.other')
+        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
+        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
+        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
+        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
+        records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4])
+
+        name = lambda record: dict(record.name_get())[record.id]
+
+        result = self.import_(['value'], [
+            ['%s,%s' % (name(records[1]), name(records[2]))],
+            ['%s,%s,%s' % (name(records[0]), name(records[1]), name(records[2]))],
+            ['%s,%s' % (name(records[0]), name(records[3]))],
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 3)
+
+        b = self.browse()
+        self.assertEqual(values(b[1].value), [3, 44, 84])
+        self.assertEqual(values(b[2].value), [3, 9])
+
+    def test_nonames(self):
+        result = self.import_(['value'], [['wherethem2mhavenonames']])
+        self.assertEqual(result['messages'], [message(
+            u"No matching record found for name 'wherethem2mhavenonames' in "
+            u"field 'unknown'", moreinfo=moreaction(
+                res_model='export.many2many.other'))])
+        self.assertIs(result['ids'], False)
+
+    def test_import_to_existing(self):
+        M2O_o = self.registry('export.many2many.other')
+        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
+        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
+        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
+        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
+
+        xid = 'myxid'
+        result = self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id1, id2)]])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+        result = self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id3, id4)]])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        b = self.browse()
+        self.assertEqual(len(b), 1)
+        # TODO: replacement of existing m2m values is correct?
+        self.assertEqual(values(b[0].value), [84, 9])
+
+class test_o2m(ImporterCase):
+    model_name = 'export.one2many'
+
+    def test_name_get(self):
+        s = u'Java is a DSL for taking large XML files and converting them ' \
+            u'to stack traces'
+        result = self.import_(
+            ['const', 'value'],
+            [['5', s]])
+        self.assertEqual(result['messages'], [message(
+            u"No matching record found for name '%s' in field 'unknown'" % s,
+            moreinfo=moreaction(res_model='export.one2many.child'))])
+        self.assertIs(result['ids'], False)
+
+    def test_single(self):
+        result = self.import_(['const', 'value/value'], [
+            ['5', '63']
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        (b,) = self.browse()
+        self.assertEqual(b.const, 5)
+        self.assertEqual(values(b.value), [63])
+
+    def test_multicore(self):
+        result = self.import_(['const', 'value/value'], [
+            ['5', '63'],
+            ['6', '64'],
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 2)
+
+        b1, b2 = self.browse()
+        self.assertEqual(b1.const, 5)
+        self.assertEqual(values(b1.value), [63])
+        self.assertEqual(b2.const, 6)
+        self.assertEqual(values(b2.value), [64])
+
+    def test_multisub(self):
+        result = self.import_(['const', 'value/value'], [
+            ['5', '63'],
+            ['', '64'],
+            ['', '65'],
+            ['', '66'],
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        (b,) = self.browse()
+        self.assertEqual(values(b.value), [63, 64, 65, 66])
+
+    def test_multi_subfields(self):
+        result = self.import_(['value/str', 'const', 'value/value'], [
+            ['this', '5', '63'],
+            ['is', '', '64'],
+            ['the', '', '65'],
+            ['rhythm', '', '66'],
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        (b,) = self.browse()
+        self.assertEqual(values(b.value), [63, 64, 65, 66])
+        self.assertEqual(
+            values(b.value, 'str'),
+            'this is the rhythm'.split())
+
+    def test_link_inline(self):
+        """ m2m-style specification for o2ms
+        """
+        id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Bf', 'value': 109
+        })
+        id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Me', 'value': 262
+        })
+
+        result = self.import_(['const', 'value/.id'], [
+            ['42', '%d,%d' % (id1, id2)]
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        [b] = self.browse()
+        self.assertEqual(b.const, 42)
+        # automatically forces link between core record and o2ms
+        self.assertEqual(values(b.value), [109, 262])
+        self.assertEqual(values(b.value, field='parent_id'), [b, b])
+
+    def test_link(self):
+        """ O2M relating to an existing record (update) force a LINK_TO as well
+        """
+        O2M = self.registry('export.one2many.child')
+        id1 = O2M.create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Bf', 'value': 109
+        })
+        id2 = O2M.create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Me', 'value': 262
+        })
+
+        result = self.import_(['const', 'value/.id'], [
+            ['42', str(id1)],
+            ['', str(id2)],
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        [b] = self.browse()
+        self.assertEqual(b.const, 42)
+        # automatically forces link between core record and o2ms
+        self.assertEqual(values(b.value), [109, 262])
+        self.assertEqual(values(b.value, field='parent_id'), [b, b])
+
+    def test_link_2(self):
+        O2M_c = self.registry('export.one2many.child')
+        id1 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Bf', 'value': 109
+        })
+        id2 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, {
+            'str': 'Me', 'value': 262
+        })
+
+        result = self.import_(['const', 'value/.id', 'value/value'], [
+            ['42', str(id1), '1'],
+            ['', str(id2), '2'],
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        [b] = self.browse()
+        self.assertEqual(b.const, 42)
+        self.assertEqual(values(b.value), [1, 2])
+        self.assertEqual(values(b.value, field='parent_id'), [b, b])
+
+class test_o2m_multiple(ImporterCase):
+    model_name = 'export.one2many.multiple'
+
+    def test_multi_mixed(self):
+        result = self.import_(['const', 'child1/value', 'child2/value'], [
+            ['5', '11', '21'],
+            ['', '12', '22'],
+            ['', '13', '23'],
+            ['', '14', ''],
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        [b] = self.browse()
+        self.assertEqual(values(b.child1), [11, 12, 13, 14])
+        self.assertEqual(values(b.child2), [21, 22, 23])
+
+    def test_multi(self):
+        result = self.import_(['const', 'child1/value', 'child2/value'], [
+            ['5', '11', '21'],
+            ['', '12', ''],
+            ['', '13', ''],
+            ['', '14', ''],
+            ['', '', '22'],
+            ['', '', '23'],
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        [b] = self.browse()
+        self.assertEqual(values(b.child1), [11, 12, 13, 14])
+        self.assertEqual(values(b.child2), [21, 22, 23])
+
+    def test_multi_fullsplit(self):
+        result = self.import_(['const', 'child1/value', 'child2/value'], [
+            ['5', '11', ''],
+            ['', '12', ''],
+            ['', '13', ''],
+            ['', '14', ''],
+            ['', '', '21'],
+            ['', '', '22'],
+            ['', '', '23'],
+        ])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+        [b] = self.browse()
+        self.assertEqual(b.const, 5)
+        self.assertEqual(values(b.child1), [11, 12, 13, 14])
+        self.assertEqual(values(b.child2), [21, 22, 23])
+
+class test_realworld(common.TransactionCase):
+    def test_bigfile(self):
+        data = json.loads(pkgutil.get_data(self.__module__, 'contacts_big.json'))
+        result = self.registry('res.partner').load(
+            self.cr, openerp.SUPERUSER_ID,
+            ['name', 'mobile', 'email', 'image'],
+            data)
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), len(data))
+
+    def test_backlink(self):
+        data = json.loads(pkgutil.get_data(self.__module__, 'contacts.json'))
+        result = self.registry('res.partner').load(
+            self.cr, openerp.SUPERUSER_ID,
+            ["name", "type", "street", "city", "country_id", "category_id",
+             "supplier", "customer", "is_company", "parent_id"],
+            data)
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), len(data))
+
+    def test_recursive_o2m(self):
+        """ The content of the o2m field's dict needs to go through conversion
+        as it may be composed of convertables or other relational fields
+        """
+        self.registry('ir.model.data').clear_caches()
+        Model = self.registry('export.one2many.recursive')
+        result = Model.load(self.cr, openerp.SUPERUSER_ID,
+            ['value', 'child/const', 'child/child1/str', 'child/child2/value'],
+            [
+                ['4', '42', 'foo', '55'],
+                ['', '43', 'bar', '56'],
+                ['', '', 'baz', ''],
+                ['', '55', 'qux', '57'],
+                ['5', '99', 'wheee', ''],
+                ['', '98', '', '12'],
+            ],
+        context=None)
+
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 2)
+
+        b = Model.browse(self.cr, openerp.SUPERUSER_ID, result['ids'], context=None)
+        self.assertEqual((b[0].value, b[1].value), (4, 5))
+
+        self.assertEqual([child.str for child in b[0].child[1].child1],
+                         ['bar', 'baz'])
+        self.assertFalse(len(b[1].child[1].child1))
+        self.assertEqual([child.value for child in b[1].child[1].child2],
+                         [12])
+
+class test_date(ImporterCase):
+    model_name = 'export.date'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.import_(['value'], []),
+            {'ids': [], 'messages': []})
+
+    def test_basic(self):
+        result = self.import_(['value'], [['2012-02-03']])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+    def test_invalid(self):
+        result = self.import_(['value'], [['not really a date']])
+        self.assertEqual(result['messages'], [
+            message(u"'not really a date' does not seem to be a valid date "
+                    u"for field 'unknown'",
+                    moreinfo=u"Use the format '2012-12-31'")])
+        self.assertIs(result['ids'], False)
+
+class test_datetime(ImporterCase):
+    model_name = 'export.datetime'
+
+    def test_empty(self):
+        self.assertEqual(
+            self.import_(['value'], []),
+            {'ids': [], 'messages': []})
+
+    def test_basic(self):
+        result = self.import_(['value'], [['2012-02-03 11:11:11']])
+        self.assertFalse(result['messages'])
+        self.assertEqual(len(result['ids']), 1)
+
+    def test_invalid(self):
+        result = self.import_(['value'], [['not really a datetime']])
+        self.assertEqual(result['messages'], [
+            message(u"'not really a datetime' does not seem to be a valid "
+                    u"datetime for field 'unknown'",
+                    moreinfo=u"Use the format '2012-12-31 23:59:59'")])
+        self.assertIs(result['ids'], False)
+
+    def test_checktz1(self):
+        """ Imported date should be interpreted as being in the tz provided by
+        the context
+        """
+        # write dummy tz in user (Asia/Hovd UTC+0700), should be superseded by
+        # context
+        self.registry('res.users').write(
+            self.cr, openerp.SUPERUSER_ID, [openerp.SUPERUSER_ID],
+            {'tz': 'Asia/Hovd'})
+
+        # UTC+1400
+        result = self.import_(
+            ['value'], [['2012-02-03 11:11:11']], {'tz': 'Pacific/Kiritimati'})
+        self.assertFalse(result['messages'])
+        self.assertEqual(
+            values(self.read(domain=[('id', 'in', result['ids'])])),
+            ['2012-02-02 21:11:11'])
+
+        # UTC-0930
+        result = self.import_(
+            ['value'], [['2012-02-03 11:11:11']], {'tz': 'Pacific/Marquesas'})
+        self.assertFalse(result['messages'])
+        self.assertEqual(
+            values(self.read(domain=[('id', 'in', result['ids'])])),
+            ['2012-02-03 20:41:11'])
+
+    def test_usertz(self):
+        """ If the context does not hold a timezone, the importing user's tz
+        should be used
+        """
+        # UTC +1000
+        self.registry('res.users').write(
+            self.cr, openerp.SUPERUSER_ID, [openerp.SUPERUSER_ID],
+            {'tz': 'Asia/Yakutsk'})
+
+        result = self.import_(
+            ['value'], [['2012-02-03 11:11:11']])
+        self.assertFalse(result['messages'])
+        self.assertEqual(
+            values(self.read(domain=[('id', 'in', result['ids'])])),
+            ['2012-02-03 01:11:11'])
+
+    def test_notz(self):
+        """ If there is no tz either in the context or on the user, falls back
+        to UTC
+        """
+        self.registry('res.users').write(
+            self.cr, openerp.SUPERUSER_ID, [openerp.SUPERUSER_ID],
+            {'tz': False})
+
+        result = self.import_(['value'], [['2012-02-03 11:11:11']])
+        self.assertFalse(result['messages'])
+        self.assertEqual(
+            values(self.read(domain=[('id', 'in', result['ids'])])),
+            ['2012-02-03 11:11:11'])
+
+class test_unique(ImporterCase):
+    model_name = 'export.unique'
+
+    @mute_logger('openerp.sql_db')
+    def test_unique(self):
+        result = self.import_(['value'], [
+            ['1'],
+            ['1'],
+            ['2'],
+            ['3'],
+            ['3'],
+        ])
+        self.assertFalse(result['ids'])
+        self.assertEqual(result['messages'], [
+            dict(message=u"The value for the field 'value' already exists. "
+                         u"This might be 'unknown' in the current model, "
+                         u"or a field of the same name in an o2m.",
+                 type='error', rows={'from': 1, 'to': 1},
+                 record=1, field='value'),
+            dict(message=u"The value for the field 'value' already exists. "
+                         u"This might be 'unknown' in the current model, "
+                         u"or a field of the same name in an o2m.",
+                 type='error', rows={'from': 4, 'to': 4},
+                 record=4, field='value'),
+        ])
diff --git a/openerp/addons/test_limits/__init__.py b/openerp/addons/test_limits/__init__.py
new file mode 100644 (file)
index 0000000..fe44871
--- /dev/null
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+import models
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_limits/__openerp__.py b/openerp/addons/test_limits/__openerp__.py
new file mode 100644 (file)
index 0000000..59972e6
--- /dev/null
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+{
+    'name': 'test-limits',
+    'version': '0.1',
+    'category': 'Tests',
+    'description': """A module with dummy methods.""",
+    'author': 'OpenERP SA',
+    'maintainer': 'OpenERP SA',
+    'website': 'http://www.openerp.com',
+    'depends': ['base'],
+    'data': ['ir.model.access.csv'],
+    'installable': True,
+    'auto_install': False,
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_limits/ir.model.access.csv b/openerp/addons/test_limits/ir.model.access.csv
new file mode 100644 (file)
index 0000000..02f1863
--- /dev/null
@@ -0,0 +1,2 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+access_test_limits_model,access_test_limits_model,model_test_limits_model,,1,1,1,1
diff --git a/openerp/addons/test_limits/models.py b/openerp/addons/test_limits/models.py
new file mode 100644 (file)
index 0000000..32e2a9a
--- /dev/null
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+import time
+
+import openerp
+
+class m(openerp.osv.osv.Model):
+    """ This model exposes a few methods that will consume between 'almost no
+        resource' and 'a lot of resource'.
+    """
+    _name = 'test.limits.model'
+
+    def consume_nothing(self, cr, uid, context=None):
+        return True
+
+    def consume_memory(self, cr, uid, size, context=None):
+        l = [0] * size
+        return True
+
+    def leak_memory(self, cr, uid, size, context=None):
+        if not hasattr(self, 'l'):
+            self.l = []
+        self.l.append([0] * size)
+        return True
+
+    def consume_time(self, cr, uid, seconds, context=None):
+        time.sleep(seconds)
+        return True
+
+    def consume_cpu_time(self, cr, uid, seconds, context=None):
+        t0 = time.clock()
+        t1 = time.clock()
+        while t1 - t0 < seconds:
+            for i in xrange(10000000):
+                x = i * i
+            t1 = time.clock()
+        return True
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_uninstall/__init__.py b/openerp/addons/test_uninstall/__init__.py
new file mode 100644 (file)
index 0000000..fe44871
--- /dev/null
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+import models
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_uninstall/__openerp__.py b/openerp/addons/test_uninstall/__openerp__.py
new file mode 100644 (file)
index 0000000..0015d5a
--- /dev/null
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+{
+    'name': 'test-uninstall',
+    'version': '0.1',
+    'category': 'Tests',
+    'description': """A module to test the uninstall feature.""",
+    'author': 'OpenERP SA',
+    'maintainer': 'OpenERP SA',
+    'website': 'http://www.openerp.com',
+    'depends': ['base'],
+    'data': ['ir.model.access.csv'],
+    'installable': True,
+    'auto_install': False,
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_uninstall/ir.model.access.csv b/openerp/addons/test_uninstall/ir.model.access.csv
new file mode 100644 (file)
index 0000000..f5b5b1f
--- /dev/null
@@ -0,0 +1,2 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+access_test_uninstall_model,access_test_uninstall_model,model_test_uninstall_model,,1,1,1,1
diff --git a/openerp/addons/test_uninstall/models.py b/openerp/addons/test_uninstall/models.py
new file mode 100644 (file)
index 0000000..96a5800
--- /dev/null
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+import openerp
+from openerp.osv import fields
+from openerp.osv.orm import Model
+
+class test_uninstall_model(Model):
+    """
+    This model uses different types of columns to make it possible to test
+    the uninstall feature of OpenERP.
+    """
+    _name = 'test_uninstall.model'
+
+    _columns = {
+        'name': fields.char('Name', size=64),
+        'ref': fields.many2one('res.users', string='User'),
+        'rel': fields.many2many('res.users', string='Users'),
+    }
+
+    _sql_constraints = [
+        ('name_uniq', 'unique (name)', 'Each name must be unique.')
+    ]
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_workflow/__init__.py b/openerp/addons/test_workflow/__init__.py
new file mode 100644 (file)
index 0000000..fe44871
--- /dev/null
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+import models
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_workflow/__openerp__.py b/openerp/addons/test_workflow/__openerp__.py
new file mode 100644 (file)
index 0000000..12cafa2
--- /dev/null
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+{
+    'name': 'test-workflow',
+    'version': '0.1',
+    'category': 'Tests',
+    'description': """A module to play with workflows.""",
+    'author': 'OpenERP SA',
+    'maintainer': 'OpenERP SA',
+    'website': 'http://www.openerp.com',
+    'depends': ['base'],
+    'data': ['data.xml', 'ir.model.access.csv'],
+    'installable': True,
+    'auto_install': False,
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_workflow/data.xml b/openerp/addons/test_workflow/data.xml
new file mode 100644 (file)
index 0000000..6299baa
--- /dev/null
@@ -0,0 +1,517 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+
+        <record id="view_test_workflow_model" model="ir.ui.view">
+            <field name="name">Test workflow</field>
+            <field name="model">test.workflow.model</field>
+            <field name="arch" type="xml">
+                <form string="Test workflow">
+                          <button name="a-b" string="a-b" type="workflow" icon="gtk-ok" colspan="1"/>
+                          <label string="a-b"/>
+                          <button name="trigger" string="trigger" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="trigger"/>
+                </form>
+           </field>
+        </record>
+
+        <record id="action_test_workflow" model="ir.actions.act_window">
+            <field name="name">Test workflow</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="res_model">test.workflow.model</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <menuitem icon="STOCK_PREFERENCES" id="base.menu_tests" name="Tests" sequence="1000000"/>
+
+        <menuitem id="menu_test_workflow" parent="base.menu_tests" name="Test workflow"/>
+
+        <menuitem id="menu_test_workflow_leaf"
+            name="Test workflow"
+            action="action_test_workflow"
+            parent="menu_test_workflow"/>
+
+
+        <record id="test_workflow_trigger_1" model="test.workflow.trigger">
+        <!-- A single trigger record, with known ID 1 -->
+        </record>
+
+        <!-- A simple workflow:
+
+            a -signal-> b -trigger-> c
+
+        -->
+        <record id="test_workflow" model="workflow">
+            <field name="name">test.workflow</field>
+            <field name="osv">test.workflow.model</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow"/>
+            <field name="flow_start">True</field>
+            <field name="name">a</field>
+            <field name="kind">function</field>
+            <field name="action">print_a()</field>
+        </record>
+        <record id="activity_b" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow"/>
+            <field name="name">b</field>
+            <field name="kind">function</field>
+            <field name="action">print_b()</field>
+        </record>
+        <record id="activity_c" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow"/>
+            <field name="flow_stop">True</field>
+            <field name="name">c</field>
+            <field name="kind">function</field>
+            <field name="action">print_c()</field>
+        </record>
+
+        <record id="trans_a_b" model="workflow.transition">
+            <field name="act_from" ref="activity_a"/>
+            <field name="act_to" ref="activity_b"/>
+            <field name="signal">a-b</field>
+        </record>
+        <record id="trans_b_c" model="workflow.transition">
+            <field name="act_from" ref="activity_b"/>
+            <field name="act_to" ref="activity_c"/>
+            <field name="condition">condition()</field>
+            <field name="trigger_model">test.workflow.trigger</field>
+            <field name="trigger_expr_id">[1]</field>
+        </record>
+
+        <!-- Workflow A (a single activity):
+
+            a
+
+        -->
+        <record id="test_workflow_a" model="workflow">
+            <field name="name">test.workflow.a</field>
+            <field name="osv">test.workflow.model.a</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_a_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_a"/>
+            <field name="flow_start">True</field>
+            <field name="flow_stop">True</field>
+            <field name="name">a</field>
+            <field name="kind">dummy</field>
+        </record>
+
+        <!-- Workflow B (a single activity):
+
+            a
+
+        The function is run when the record is created.
+
+        -->
+        <record id="test_workflow_b" model="workflow">
+            <field name="name">test.workflow.b</field>
+            <field name="osv">test.workflow.model.b</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_b_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_b"/>
+            <field name="flow_start">True</field>
+            <field name="flow_stop">True</field>
+            <field name="name">a</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 1})</field>
+        </record>
+
+        <!-- Workflow C (a single activity):
+
+            a
+
+        The function is not run when the kind is dummy and no action_id is provided.
+        -->
+        <record id="test_workflow_c" model="workflow">
+            <field name="name">test.workflow.c</field>
+            <field name="osv">test.workflow.model.c</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_c_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_c"/>
+            <field name="flow_start">True</field>
+            <field name="flow_stop">True</field>
+            <field name="name">a</field>
+            <field name="kind">dummy</field>
+            <field name="action">write({'value': 1})</field>
+        </record>
+
+        <!-- Workflow D (a single activity):
+
+            a
+
+        The function is run when the kind is stopall and no action_id is provided.
+        -->
+        <record id="test_workflow_d" model="workflow">
+            <field name="name">test.workflow.d</field>
+            <field name="osv">test.workflow.model.d</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_d_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_d"/>
+            <field name="flow_start">True</field>
+            <field name="flow_stop">True</field>
+            <field name="name">a</field>
+            <field name="kind">stopall</field>
+            <field name="action">write({'value': 1})</field>
+        </record>
+
+        <!-- Workflow E:
+
+            a -True-> b
+
+        Both activities are run when the workflow is instanciated.
+        -->
+        <record id="test_workflow_e" model="workflow">
+            <field name="name">test.workflow.e</field>
+            <field name="osv">test.workflow.model.e</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_e_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_e"/>
+            <field name="flow_start">True</field>
+            <field name="name">a</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 1})</field>
+        </record>
+        <record id="activity_e_b" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_e"/>
+            <field name="flow_stop">True</field>
+            <field name="name">b</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 2})</field>
+        </record>
+
+        <record id="trans_e_a_b" model="workflow.transition">
+            <field name="act_from" ref="activity_e_a"/>
+            <field name="act_to" ref="activity_e_b"/>
+        </record>
+
+        <!-- Workflow F:
+
+            a -signal-> b
+
+        Same as E but with a signal on the transition.
+        -->
+        <record id="test_workflow_f" model="workflow">
+            <field name="name">test.workflow.f</field>
+            <field name="osv">test.workflow.model.f</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_f_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_f"/>
+            <field name="flow_start">True</field>
+            <field name="name">a</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 1})</field>
+        </record>
+        <record id="activity_f_b" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_f"/>
+            <field name="flow_stop">True</field>
+            <field name="name">b</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 2})</field>
+        </record>
+
+        <record id="trans_f_a_b" model="workflow.transition">
+            <field name="act_from" ref="activity_f_a"/>
+            <field name="act_to" ref="activity_f_b"/>
+            <field name="signal">a-b</field>
+        </record>
+
+        <!-- Workflow G:
+
+            a -False-> b
+
+        -->
+        <record id="test_workflow_g" model="workflow">
+            <field name="name">test.workflow.g</field>
+            <field name="osv">test.workflow.model.g</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_g_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_g"/>
+            <field name="flow_start">True</field>
+            <field name="name">a</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 1})</field>
+        </record>
+        <record id="activity_g_b" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_g"/>
+            <field name="flow_stop">True</field>
+            <field name="name">b</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 2})</field>
+        </record>
+
+        <record id="trans_g_a_b" model="workflow.transition">
+            <field name="act_from" ref="activity_g_a"/>
+            <field name="act_to" ref="activity_g_b"/>
+            <field name="condition">False</field>
+        </record>
+
+        <!-- Workflow H:
+
+            a or  -> b { value: 2 } 
+                 `-> c { value: 2 }
+
+        Whether the action of b or c is exectued last is non-deterministic.
+
+        -->
+        <record id="test_workflow_h" model="workflow">
+            <field name="name">test.workflow.h</field>
+            <field name="osv">test.workflow.model.h</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_h_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_h"/>
+            <field name="flow_start">True</field>
+            <field name="name">a</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 1})</field>
+            <field name="split_mode">OR</field>
+        </record>
+        <record id="activity_h_b" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_h"/>
+            <field name="flow_stop">True</field>
+            <field name="name">b</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 2})</field>
+        </record>
+        <record id="activity_h_c" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_h"/>
+            <field name="flow_stop">True</field>
+            <field name="name">c</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 2})</field>
+        </record>
+
+        <record id="trans_h_a_b" model="workflow.transition">
+            <field name="act_from" ref="activity_h_a"/>
+            <field name="act_to" ref="activity_h_b"/>
+        </record>
+        <record id="trans_h_a_c" model="workflow.transition">
+            <field name="act_from" ref="activity_h_a"/>
+            <field name="act_to" ref="activity_h_c"/>
+        </record>
+
+        <!-- Workflow I:
+
+            a or -> b { value: 2 } 
+                 `false> c { value: 3 }
+
+        -->
+        <record id="test_workflow_i" model="workflow">
+            <field name="name">test.workflow.i</field>
+            <field name="osv">test.workflow.model.i</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_i_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_i"/>
+            <field name="flow_start">True</field>
+            <field name="name">a</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 1})</field>
+            <field name="split_mode">OR</field>
+        </record>
+        <record id="activity_i_b" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_i"/>
+            <field name="flow_stop">True</field>
+            <field name="name">b</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 2})</field>
+        </record>
+        <record id="activity_i_c" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_i"/>
+            <field name="flow_stop">True</field>
+            <field name="name">c</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 3})</field>
+        </record>
+
+        <record id="trans_i_a_b" model="workflow.transition">
+            <field name="act_from" ref="activity_i_a"/>
+            <field name="act_to" ref="activity_i_b"/>
+        </record>
+        <record id="trans_i_a_c" model="workflow.transition">
+            <field name="act_from" ref="activity_i_a"/>
+            <field name="act_to" ref="activity_i_c"/>
+            <field name="condition">False</field>
+        </record>
+
+        <!-- Workflow J:
+
+            a and -> b { value: 2 } 
+                  `False> c { value: 3 }
+
+        This will stay in a because all transitions should be True at the same time.
+
+        -->
+        <record id="test_workflow_j" model="workflow">
+            <field name="name">test.workflow.j</field>
+            <field name="osv">test.workflow.model.j</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_j_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_j"/>
+            <field name="flow_start">True</field>
+            <field name="name">a</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 1})</field>
+            <field name="split_mode">AND</field>
+        </record>
+        <record id="activity_j_b" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_j"/>
+            <field name="flow_stop">True</field>
+            <field name="name">b</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 2})</field>
+        </record>
+        <record id="activity_j_c" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_j"/>
+            <field name="flow_stop">True</field>
+            <field name="name">c</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 3})</field>
+        </record>
+
+        <record id="trans_j_a_b" model="workflow.transition">
+            <field name="act_from" ref="activity_j_a"/>
+            <field name="act_to" ref="activity_j_b"/>
+        </record>
+        <record id="trans_j_a_c" model="workflow.transition">
+            <field name="act_from" ref="activity_j_a"/>
+            <field name="act_to" ref="activity_j_c"/>
+            <field name="condition">False</field>
+        </record>
+
+        <!-- Workflow K:
+
+            a xor -> b { value: 2 } 
+                  `> c { value: 2 }
+
+        Only one (truish) transition is taken with a XOR.
+
+        -->
+        <record id="test_workflow_k" model="workflow">
+            <field name="name">test.workflow.k</field>
+            <field name="osv">test.workflow.model.k</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_k_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_k"/>
+            <field name="flow_start">True</field>
+            <field name="name">a</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 1})</field>
+            <field name="split_mode">XOR</field>
+        </record>
+        <record id="activity_k_b" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_k"/>
+            <field name="flow_stop">True</field>
+            <field name="name">b</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 2})</field>
+        </record>
+        <record id="activity_k_c" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_k"/>
+            <field name="flow_stop">True</field>
+            <field name="name">c</field>
+            <field name="kind">function</field>
+           <field name="action">write({'value': 2})</field>
+        </record>
+
+        <record id="trans_k_a_b" model="workflow.transition">
+            <field name="act_from" ref="activity_k_a"/>
+            <field name="act_to" ref="activity_k_b"/>
+        </record>
+        <record id="trans_k_a_c" model="workflow.transition">
+            <field name="act_from" ref="activity_k_a"/>
+            <field name="act_to" ref="activity_k_c"/>
+        </record>
+
+        <!-- Workflow L:
+
+            a -> xor c { value: 3 }
+            b ´
+            a -> and d { value: 3 }
+            b ´
+
+        c is run for each incoming (and taken) transition.
+        d is run once when all its incoming transitions are taken at the same time.
+
+        -->
+        <record id="test_workflow_l" model="workflow">
+            <field name="name">test.workflow.l</field>
+            <field name="osv">test.workflow.model.l</field>
+            <field name="on_create">True</field>
+        </record>
+
+        <record id="activity_l_a" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_l"/>
+            <field name="flow_start">True</field>
+            <field name="name">a</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 1})</field>
+            <field name="split_mode">OR</field>
+        </record>
+        <record id="activity_l_b" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_l"/>
+            <field name="flow_start">True</field>
+            <field name="name">b</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 2})</field>
+            <field name="split_mode">OR</field>
+        </record>
+        <record id="activity_l_c" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_l"/>
+            <field name="flow_stop">True</field>
+            <field name="name">c</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 3})</field>
+            <field name="join_mode">XOR</field>
+        </record>
+        <record id="activity_l_d" model="workflow.activity">
+            <field name="wkf_id" ref="test_workflow_l"/>
+            <field name="flow_stop">True</field>
+            <field name="name">d</field>
+            <field name="kind">function</field>
+            <field name="action">write({'value': 3})</field>
+            <field name="join_mode">AND</field>
+        </record>
+
+        <record id="trans_l_a_c" model="workflow.transition">
+            <field name="act_from" ref="activity_l_a"/>
+            <field name="act_to" ref="activity_l_c"/>
+        </record>
+        <record id="trans_l_b_c" model="workflow.transition">
+            <field name="act_from" ref="activity_l_b"/>
+            <field name="act_to" ref="activity_l_c"/>
+        </record>
+
+        <record id="trans_l_a_d" model="workflow.transition">
+            <field name="act_from" ref="activity_l_a"/>
+            <field name="act_to" ref="activity_l_d"/>
+        </record>
+        <record id="trans_l_b_d" model="workflow.transition">
+            <field name="act_from" ref="activity_l_b"/>
+            <field name="act_to" ref="activity_l_d"/>
+        </record>
+    </data>
+</openerp>
diff --git a/openerp/addons/test_workflow/ir.model.access.csv b/openerp/addons/test_workflow/ir.model.access.csv
new file mode 100644 (file)
index 0000000..6fb5a7a
--- /dev/null
@@ -0,0 +1,15 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_test_workflow_model,access_test_workflow_model,model_test_workflow_model,,1,1,1,1
+access_test_workflow_trigger,access_test_workflow_trigger,model_test_workflow_trigger,,1,1,1,1
+access_test_workflow_model_a,access_test_workflow_model_a,model_test_workflow_model_a,,1,1,1,1
+access_test_workflow_model_b,access_test_workflow_model_b,model_test_workflow_model_b,,1,1,1,1
+access_test_workflow_model_c,access_test_workflow_model_c,model_test_workflow_model_c,,1,1,1,1
+access_test_workflow_model_d,access_test_workflow_model_d,model_test_workflow_model_d,,1,1,1,1
+access_test_workflow_model_e,access_test_workflow_model_e,model_test_workflow_model_e,,1,1,1,1
+access_test_workflow_model_f,access_test_workflow_model_f,model_test_workflow_model_f,,1,1,1,1
+access_test_workflow_model_g,access_test_workflow_model_g,model_test_workflow_model_g,,1,1,1,1
+access_test_workflow_model_h,access_test_workflow_model_h,model_test_workflow_model_h,,1,1,1,1
+access_test_workflow_model_i,access_test_workflow_model_i,model_test_workflow_model_i,,1,1,1,1
+access_test_workflow_model_j,access_test_workflow_model_j,model_test_workflow_model_j,,1,1,1,1
+access_test_workflow_model_k,access_test_workflow_model_k,model_test_workflow_model_k,,1,1,1,1
+access_test_workflow_model_l,access_test_workflow_model_l,model_test_workflow_model_l,,1,1,1,1
diff --git a/openerp/addons/test_workflow/models.py b/openerp/addons/test_workflow/models.py
new file mode 100644 (file)
index 0000000..f7a5ef2
--- /dev/null
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+import openerp
+
+class m(openerp.osv.orm.Model):
+    """ A model for which we will define a workflow (see data.xml). """
+    _name = 'test.workflow.model'
+
+    def print_(self, cr, uid, ids, s, context=None):
+        print '  Running activity `%s` for record %s' % (s, ids)
+        return True
+
+    def print_a(self, cr, uid, ids, context=None):
+        return self.print_(cr, uid, ids, 'a', context)
+
+    def print_b(self, cr, uid, ids, context=None):
+        return self.print_(cr, uid, ids, 'b', context)
+
+    def print_c(self, cr, uid, ids, context=None):
+        return self.print_(cr, uid, ids, 'c', context)
+
+    def condition(self, cr, uid, ids, context=None):
+        m = self.pool['test.workflow.trigger']
+        for r in m.browse(cr, uid, [1], context=context):
+            if not r.value:
+                return False
+        return True
+
+    def trigger(self, cr, uid, context=None):
+        return openerp.workflow.trg_trigger(uid, 'test.workflow.trigger', 1, cr)
+
+class n(openerp.osv.orm.Model):
+    """ A model used for the trigger feature. """
+    _name = 'test.workflow.trigger'
+    _columns = { 'value': openerp.osv.fields.boolean('Value') }
+    _defaults = { 'value': False }
+
+class a(openerp.osv.orm.Model):
+    _name = 'test.workflow.model.a'
+    _columns = { 'value': openerp.osv.fields.integer('Value') }
+    _defaults = { 'value': 0 }
+
+class b(openerp.osv.orm.Model):
+    _name = 'test.workflow.model.b'
+    _inherit = 'test.workflow.model.a'
+
+class c(openerp.osv.orm.Model):
+    _name = 'test.workflow.model.c'
+    _inherit = 'test.workflow.model.a'
+
+class d(openerp.osv.orm.Model):
+    _name = 'test.workflow.model.d'
+    _inherit = 'test.workflow.model.a'
+
+class e(openerp.osv.orm.Model):
+    _name = 'test.workflow.model.e'
+    _inherit = 'test.workflow.model.a'
+
+for name in 'bcdefghijkl':
+    type(
+        name,
+        (openerp.osv.orm.Model,),
+        {
+            '_name': 'test.workflow.model.%s' % name,
+            '_inherit': 'test.workflow.model.a',
+        })
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_workflow/tests/__init__.py b/openerp/addons/test_workflow/tests/__init__.py
new file mode 100644 (file)
index 0000000..a09d09f
--- /dev/null
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+
+from . import test_workflow
+
+fast_suite = [
+]
+
+checks = [
+    test_workflow,
+]
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/test_workflow/tests/test_workflow.py b/openerp/addons/test_workflow/tests/test_workflow.py
new file mode 100644 (file)
index 0000000..e9ff7b9
--- /dev/null
@@ -0,0 +1,183 @@
+# -*- coding: utf-8 -*-
+import openerp
+from openerp import SUPERUSER_ID
+from openerp.tests import common
+
+
+class test_workflows(common.TransactionCase):
+
+    def check_activities(self, model_name, i, names):
+        """ Check that the record i has workitems in the given activity names.
+        """
+        instance = self.registry('workflow.instance')
+        workitem = self.registry('workflow.workitem')
+
+        # Given the workflow instance associated to the record ...
+        instance_id = instance.search(
+            self.cr, SUPERUSER_ID,
+            [('res_type', '=', model_name), ('res_id', '=', i)])
+        self.assertTrue( instance_id, 'A workflow instance is expected.')
+
+        # ... get all its workitems ...
+        workitem_ids = workitem.search(
+            self.cr, SUPERUSER_ID,
+            [('inst_id', '=', instance_id[0])])
+        self.assertTrue(
+            workitem_ids,
+            'The workflow instance should have workitems.')
+
+        # ... and check the activity the are in against the provided names.
+        workitem_records = workitem.browse(
+            self.cr, SUPERUSER_ID, workitem_ids)
+        self.assertEqual(
+            sorted([item.act_id.name for item in workitem_records]),
+            sorted(names))
+
+    def check_value(self, model_name, i, value):
+        """ Check that the record i has the given value.
+        """
+        model = self.registry(model_name)
+        record = model.read(self.cr, SUPERUSER_ID, [i], ['value'])[0]
+        self.assertEqual(record['value'], value)
+
+    def test_workflow(self):
+        model = self.registry('test.workflow.model')
+        trigger = self.registry('test.workflow.trigger')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['a'])
+
+        # a -> b is just a signal.
+        model.signal_workflow(self.cr, SUPERUSER_ID, [i], 'a-b')
+        self.check_activities(model._name, i, ['b'])
+
+        # b -> c is a trigger (which is False),
+        # so we remain in the b activity.
+        model.trigger(self.cr, SUPERUSER_ID, [i])
+        self.check_activities(model._name, i, ['b'])
+
+        # b -> c is a trigger (which is set to True).
+        # so we go in c when the trigger is called.
+        trigger.write(self.cr, SUPERUSER_ID, [1], {'value': True})
+        model.trigger(self.cr, SUPERUSER_ID)
+        self.check_activities(model._name, i, ['c'])
+
+        self.assertEqual(
+            True,
+            True)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_a(self):
+        model = self.registry('test.workflow.model.a')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['a'])
+        self.check_value(model._name, i, 0)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_b(self):
+        model = self.registry('test.workflow.model.b')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['a'])
+        self.check_value(model._name, i, 1)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_c(self):
+        model = self.registry('test.workflow.model.c')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['a'])
+        self.check_value(model._name, i, 0)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_d(self):
+        model = self.registry('test.workflow.model.d')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['a'])
+        self.check_value(model._name, i, 1)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_e(self):
+        model = self.registry('test.workflow.model.e')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['b'])
+        self.check_value(model._name, i, 2)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_f(self):
+        model = self.registry('test.workflow.model.f')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['a'])
+        self.check_value(model._name, i, 1)
+
+        model.signal_workflow(self.cr, SUPERUSER_ID, [i], 'a-b')
+        self.check_activities(model._name, i, ['b'])
+        self.check_value(model._name, i, 2)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_g(self):
+        model = self.registry('test.workflow.model.g')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['a'])
+        self.check_value(model._name, i, 1)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_h(self):
+        model = self.registry('test.workflow.model.h')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['b', 'c'])
+        self.check_value(model._name, i, 2)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_i(self):
+        model = self.registry('test.workflow.model.i')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['b'])
+        self.check_value(model._name, i, 2)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_j(self):
+        model = self.registry('test.workflow.model.j')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['a'])
+        self.check_value(model._name, i, 1)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_k(self):
+        model = self.registry('test.workflow.model.k')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        # Non-determinisitic: can be b or c
+        # self.check_activities(model._name, i, ['b'])
+        # self.check_activities(model._name, i, ['c'])
+        self.check_value(model._name, i, 2)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
+
+    def test_workflow_l(self):
+        model = self.registry('test.workflow.model.l')
+
+        i = model.create(self.cr, SUPERUSER_ID, {})
+        self.check_activities(model._name, i, ['c', 'c', 'd'])
+        self.check_value(model._name, i, 3)
+
+        model.unlink(self.cr, SUPERUSER_ID, [i])
index de9d21d..d8dc5c7 100644 (file)
@@ -282,6 +282,9 @@ class Cursor(object):
         return self._close(False)
 
     def _close(self, leak=False):
+        #import traceback
+        #traceback.print_stack()
+
         if not self._obj:
             return
 
index a741224..80b3933 100644 (file)
@@ -7,43 +7,9 @@ This module groups a few sub-modules containing unittest2 test cases.
 Tests can be explicitely added to the `fast_suite` or `checks` lists or not.
 See the :ref:`test-framework` section in the :ref:`features` list.
 """
-import test_acl
-import test_basecase
-import test_db_cursor
-import test_expression
-import test_fields
-import test_ir_filters
-import test_ir_sequence
-import test_mail
-import test_misc
-import test_orm
-import test_osv
-import test_translate
-import test_view_validation
-import test_qweb
-import test_func
-# This need a change in `oe run-tests` to only run fast_suite + checks by default.
-# import test_xmlrpc
 
-fast_suite = [
-    test_ir_sequence,
-    test_ir_filters
-]
+import common
+from common import *
+
 
-checks = [
-    test_acl,
-    test_expression,
-    test_mail,
-    test_db_cursor,
-    test_orm,
-    test_fields,
-    test_basecase,
-    test_view_validation,
-    test_misc,
-    test_osv,
-    test_translate,
-    test_qweb,
-    test_func,
-]
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_convert/__init__.py b/openerp/tests/addons/test_convert/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/openerp/tests/addons/test_convert/__openerp__.py b/openerp/tests/addons/test_convert/__openerp__.py
deleted file mode 100644 (file)
index 40d9eb3..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-    'name': 'test_convert',
-    'description': "Data for xml conversion tests",
-    'version': '0.0.1',
-}
diff --git a/openerp/tests/addons/test_convert/test_file.txt b/openerp/tests/addons/test_convert/test_file.txt
deleted file mode 100644 (file)
index e69b2e0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-nothing to see here, move along
diff --git a/openerp/tests/addons/test_convert/tests/__init__.py b/openerp/tests/addons/test_convert/tests/__init__.py
deleted file mode 100644 (file)
index 002a0a1..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# -*- coding: utf-8 -*-
-from . import test_convert
-
-checks = [
-    test_convert
-]
diff --git a/openerp/tests/addons/test_convert/tests/test_convert.py b/openerp/tests/addons/test_convert/tests/test_convert.py
deleted file mode 100644 (file)
index 50d7df4..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-import collections
-import unittest2
-from lxml import etree as ET
-from lxml.builder import E
-
-from openerp.tests import common
-
-from openerp.tools.convert import _eval_xml
-
-Field = E.field
-Value = E.value
-class TestEvalXML(common.TransactionCase):
-    def eval_xml(self, node, obj=None, idref=None):
-        return _eval_xml(obj, node, pool=None, cr=self.cr, uid=self.uid,
-                         idref=idref, context=None)
-
-    def test_char(self):
-        self.assertEqual(
-            self.eval_xml(Field("foo")),
-            "foo")
-        self.assertEqual(
-            self.eval_xml(Field("None")),
-            "None")
-
-    def test_int(self):
-        self.assertIsNone(
-            self.eval_xml(Field("None", type='int')),
-            "what the fuck?")
-        self.assertEqual(
-            self.eval_xml(Field(" 42  ", type="int")),
-            42)
-
-        with self.assertRaises(ValueError):
-            self.eval_xml(Field("4.82", type="int"))
-
-        with self.assertRaises(ValueError):
-            self.eval_xml(Field("Whelp", type="int"))
-
-    def test_float(self):
-        self.assertEqual(
-            self.eval_xml(Field("4.78", type="float")),
-            4.78)
-
-        with self.assertRaises(ValueError):
-            self.eval_xml(Field("None", type="float"))
-
-        with self.assertRaises(ValueError):
-            self.eval_xml(Field("Foo", type="float"))
-
-    def test_list(self):
-        self.assertEqual(
-            self.eval_xml(Field(type="list")),
-            [])
-
-        self.assertEqual(
-            self.eval_xml(Field(
-                Value("foo"),
-                Value("5", type="int"),
-                Value("4.76", type="float"),
-                Value("None", type="int"),
-                type="list"
-            )),
-            ["foo", 5, 4.76, None])
-
-    def test_file(self):
-        Obj = collections.namedtuple('Obj', 'module')
-        obj = Obj('test_convert')
-        self.assertEqual(
-            self.eval_xml(Field('test_file.txt', type='file'), obj),
-            'test_convert,test_file.txt')
-
-        with self.assertRaises(IOError):
-            self.eval_xml(Field('test_nofile.txt', type='file'), obj)
-
-    @unittest2.skip("not tested")
-    def test_xml(self):
-        pass
-
-    @unittest2.skip("not tested")
-    def test_html(self):
-        pass
-
-
diff --git a/openerp/tests/addons/test_converter/__init__.py b/openerp/tests/addons/test_converter/__init__.py
deleted file mode 100644 (file)
index 89d26e2..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-# -*- coding: utf-8 -*-
-import models
diff --git a/openerp/tests/addons/test_converter/__openerp__.py b/openerp/tests/addons/test_converter/__openerp__.py
deleted file mode 100644 (file)
index c76b1fe..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-{
-    'name': 'test-field-converter',
-    'version': '0.1',
-    'category': 'Tests',
-    'description': """Tests of field conversions""",
-    'author': 'OpenERP SA',
-    'maintainer': 'OpenERP SA',
-    'website': 'http://www.openerp.com',
-    'depends': ['base'],
-    'data': ['ir.model.access.csv'],
-    'installable': True,
-    'auto_install': False,
-}
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_converter/ir.model.access.csv b/openerp/tests/addons/test_converter/ir.model.access.csv
deleted file mode 100644 (file)
index ff4ac97..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_converter_model,access_converter_model,model_test_converter_test_model,,1,1,1,1
-access_test_converter_test_model_sub,access_test_converter_test_model_sub,model_test_converter_test_model_sub,,1,1,1,1
-access_test_converter_monetary,access_test_converter_monetary,model_test_converter_monetary,,1,1,1,1
diff --git a/openerp/tests/addons/test_converter/models.py b/openerp/tests/addons/test_converter/models.py
deleted file mode 100644 (file)
index 9074ba8..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-# -*- coding: utf-8 -*-
-from openerp.osv import orm, fields
-
-class test_model(orm.Model):
-    _name = 'test_converter.test_model'
-
-    _columns = {
-        'char': fields.char(),
-        'integer': fields.integer(),
-        'float': fields.float(),
-        'numeric': fields.float(digits=(16, 2)),
-        'many2one': fields.many2one('test_converter.test_model.sub'),
-        'binary': fields.binary(),
-        'date': fields.date(),
-        'datetime': fields.datetime(),
-        'selection': fields.selection([
-            (1, "réponse A"),
-            (2, "réponse B"),
-            (3, "réponse C"),
-            (4, "réponse D"),
-        ]),
-        'selection_str': fields.selection([
-            ('A', "Qu'il n'est pas arrivé à Toronto"),
-            ('B', "Qu'il était supposé arriver à Toronto"),
-            ('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
-            ('D', "La réponse D"),
-        ], string="Lorsqu'un pancake prend l'avion à destination de Toronto et "
-                  "qu'il fait une escale technique à St Claude, on dit:"),
-        'html': fields.html(),
-        'text': fields.text(),
-    }
-
-class test_model_sub(orm.Model):
-    _name = 'test_converter.test_model.sub'
-
-    _columns = {
-        'name': fields.char()
-    }
-
-
-class test_model_monetary(orm.Model):
-    _name = 'test_converter.monetary'
-
-    _columns = {
-        'value': fields.float(digits=(16, 55)),
-    }
diff --git a/openerp/tests/addons/test_converter/tests/__init__.py b/openerp/tests/addons/test_converter/tests/__init__.py
deleted file mode 100644 (file)
index 493b164..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from . import test_html
-
-fast_suite = [
-]
-
-checks = [
-    test_html
-]
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_converter/tests/test_html.py b/openerp/tests/addons/test_converter/tests/test_html.py
deleted file mode 100644 (file)
index 2f08969..0000000
+++ /dev/null
@@ -1,387 +0,0 @@
-# -*- encoding: utf-8 -*-
-import json
-import os
-import xml.dom.minidom
-import datetime
-
-from werkzeug.utils import escape as e
-
-from openerp.tests import common
-from openerp.addons.base.ir import ir_qweb
-
-directory = os.path.dirname(__file__)
-
-impl = xml.dom.minidom.getDOMImplementation()
-doc = impl.createDocument(None, None, None)
-
-class TestExport(common.TransactionCase):
-    _model = None
-
-    def setUp(self):
-        super(TestExport, self).setUp()
-        self.Model = self.registry(self._model)
-        self.columns = self.Model._all_columns
-
-    def get_column(self, name):
-        return self.Model._all_columns[name].column
-
-    def get_converter(self, name, type=None):
-        column = self.get_column(name)
-
-        for postfix in type, column._type, '':
-            fs = ['ir', 'qweb', 'field']
-            if postfix is None: continue
-            if postfix: fs.append(postfix)
-
-            try:
-                model = self.registry('.'.join(fs))
-                break
-            except KeyError: pass
-
-        return lambda value, options=None, context=None: e(model.value_to_html(
-            self.cr, self.uid, value, column, options=options, context=context))
-
-class TestBasicExport(TestExport):
-    _model = 'test_converter.test_model'
-
-class TestCharExport(TestBasicExport):
-    def test_char(self):
-        converter = self.get_converter('char')
-
-        value = converter('foo')
-        self.assertEqual(value, 'foo')
-
-        value = converter("foo<bar>")
-        self.assertEqual(value, "foo&lt;bar&gt;")
-
-class TestIntegerExport(TestBasicExport):
-    def test_integer(self):
-        converter = self.get_converter('integer')
-
-        value = converter(42)
-        self.assertEqual(value, "42")
-
-class TestFloatExport(TestBasicExport):
-    def setUp(self):
-        super(TestFloatExport, self).setUp()
-        self.registry('res.lang').write(self.cr, self.uid, [1], {
-            'grouping': '[3,0]'
-        })
-
-    def test_float(self):
-        converter = self.get_converter('float')
-
-        value = converter(42.0)
-        self.assertEqual(value, "42.0")
-
-        value = converter(42.0100)
-        self.assertEqual(value, "42.01")
-
-        value = converter(42.01234)
-        self.assertEqual(value, "42.01234")
-
-        value = converter(1234567.89)
-        self.assertEqual(value, '1,234,567.89')
-
-    def test_numeric(self):
-        converter = self.get_converter('numeric')
-
-        value = converter(42.0)
-        self.assertEqual(value, '42.00')
-
-        value = converter(42.01234)
-        self.assertEqual(value, '42.01')
-
-class TestCurrencyExport(TestExport):
-    _model = 'test_converter.monetary'
-
-    def setUp(self):
-        super(TestCurrencyExport, self).setUp()
-        self.Currency = self.registry('res.currency')
-        self.base = self.create(self.Currency, name="Source", symbol=u'source')
-
-    def create(self, model, context=None, **values):
-        return model.browse(
-            self.cr, self.uid,
-            model.create(self.cr, self.uid, values, context=context),
-            context=context)
-
-    def convert(self, obj, dest):
-        converter = self.registry('ir.qweb.field.monetary')
-        options = {
-            'widget': 'monetary',
-            'display_currency': 'c2'
-        }
-        converted = converter.to_html(
-            self.cr, self.uid, 'value', obj, options,
-            doc.createElement('span'),
-            {'field': 'obj.value', 'field-options': json.dumps(options)},
-            '', ir_qweb.QWebContext(self.cr, self.uid, {'obj': obj, 'c2': dest, }))
-        return converted
-
-    def test_currency_post(self):
-        currency = self.create(self.Currency, name="Test", symbol=u"test")
-        obj = self.create(self.Model, value=0.12)
-
-        converted = self.convert(obj, dest=currency)
-
-        self.assertEqual(
-            converted,
-            '<span data-oe-model="{obj._model._name}" data-oe-id="{obj.id}" '
-                  'data-oe-field="value" data-oe-type="monetary" '
-                  'data-oe-expression="obj.value">'
-                      '<span class="oe_currency_value">0.12</span>'
-                      ' {symbol}</span>'.format(
-                obj=obj,
-                symbol=currency.symbol.encode('utf-8')
-            ),)
-
-    def test_currency_pre(self):
-        currency = self.create(
-            self.Currency, name="Test", symbol=u"test", position='before')
-        obj = self.create(self.Model, value=0.12)
-
-        converted = self.convert(obj, dest=currency)
-
-        self.assertEqual(
-            converted,
-            '<span data-oe-model="{obj._model._name}" data-oe-id="{obj.id}" '
-                  'data-oe-field="value" data-oe-type="monetary" '
-                  'data-oe-expression="obj.value">'
-                      '{symbol} '
-                      '<span class="oe_currency_value">0.12</span>'
-                      '</span>'.format(
-                obj=obj,
-                symbol=currency.symbol.encode('utf-8')
-            ),)
-
-    def test_currency_precision(self):
-        """ Precision should be the currency's, not the float field's
-        """
-        currency = self.create(self.Currency, name="Test", symbol=u"test",)
-        obj = self.create(self.Model, value=0.1234567)
-
-        converted = self.convert(obj, dest=currency)
-
-        self.assertEqual(
-            converted,
-            '<span data-oe-model="{obj._model._name}" data-oe-id="{obj.id}" '
-                  'data-oe-field="value" data-oe-type="monetary" '
-                  'data-oe-expression="obj.value">'
-                      '<span class="oe_currency_value">0.12</span>'
-                      ' {symbol}</span>'.format(
-                obj=obj,
-                symbol=currency.symbol.encode('utf-8')
-            ),)
-
-class TestTextExport(TestBasicExport):
-    def test_text(self):
-        converter = self.get_converter('text')
-
-        value = converter("This is my text-kai")
-        self.assertEqual(value, "This is my text-kai")
-
-        value = converter("""
-            .  The current line (address) in the buffer.
-            $  The last line in the buffer.
-            n  The nth, line in the buffer where n is a number in the range [0,$].
-            $  The last line in the buffer.
-            -  The previous line. This is equivalent to -1 and may be repeated with cumulative effect.
-            -n The nth previous line, where n is a non-negative number.
-            +  The next line. This is equivalent to +1 and may be repeated with cumulative effect.
-        """)
-        self.assertEqual(value, """<br>
-            .  The current line (address) in the buffer.<br>
-            $  The last line in the buffer.<br>
-            n  The nth, line in the buffer where n is a number in the range [0,$].<br>
-            $  The last line in the buffer.<br>
-            -  The previous line. This is equivalent to -1 and may be repeated with cumulative effect.<br>
-            -n The nth previous line, where n is a non-negative number.<br>
-            +  The next line. This is equivalent to +1 and may be repeated with cumulative effect.<br>
-        """)
-
-        value = converter("""
-        fgdkls;hjas;lj <b>fdslkj</b> d;lasjfa lkdja <a href=http://spam.com>lfks</a>
-        fldkjsfhs <i style="color: red"><a href="http://spamspam.com">fldskjh</a></i>
-        """)
-        self.assertEqual(value, """<br>
-        fgdkls;hjas;lj &lt;b&gt;fdslkj&lt;/b&gt; d;lasjfa lkdja &lt;a href=http://spam.com&gt;lfks&lt;/a&gt;<br>
-        fldkjsfhs &lt;i style=&quot;color: red&quot;&gt;&lt;a href=&quot;http://spamspam.com&quot;&gt;fldskjh&lt;/a&gt;&lt;/i&gt;<br>
-        """)
-
-class TestMany2OneExport(TestBasicExport):
-    def test_many2one(self):
-        Sub = self.registry('test_converter.test_model.sub')
-
-
-        id0 = self.Model.create(self.cr, self.uid, {
-            'many2one': Sub.create(self.cr, self.uid, {'name': "Foo"})
-        })
-        id1 = self.Model.create(self.cr, self.uid, {
-            'many2one': Sub.create(self.cr, self.uid, {'name': "Fo<b>o</b>"})
-        })
-
-        def converter(record):
-            column = self.get_column('many2one')
-            model = self.registry('ir.qweb.field.many2one')
-
-            return e(model.record_to_html(
-                self.cr, self.uid, 'many2one', record, column))
-
-        value = converter(self.Model.browse(self.cr, self.uid, id0))
-        self.assertEqual(value, "Foo")
-
-        value = converter(self.Model.browse(self.cr, self.uid, id1))
-        self.assertEqual(value, "Fo&lt;b&gt;o&lt;/b&gt;")
-
-class TestBinaryExport(TestBasicExport):
-    def test_image(self):
-        column = self.get_column('binary')
-        converter = self.registry('ir.qweb.field.image')
-
-        with open(os.path.join(directory, 'test_vectors', 'image'), 'rb') as f:
-            content = f.read()
-
-        encoded_content = content.encode('base64')
-        value = e(converter.value_to_html(
-            self.cr, self.uid, encoded_content, column))
-        self.assertEqual(
-            value, '<img src="data:image/jpeg;base64,%s">' % (
-                encoded_content
-            ))
-
-        with open(os.path.join(directory, 'test_vectors', 'pdf'), 'rb') as f:
-            content = f.read()
-
-        with self.assertRaises(ValueError):
-            e(converter.value_to_html(
-                self.cr, self.uid, 'binary', content.encode('base64'), column))
-
-        with open(os.path.join(directory, 'test_vectors', 'pptx'), 'rb') as f:
-            content = f.read()
-
-        with self.assertRaises(ValueError):
-            e(converter.value_to_html(
-                self.cr, self.uid, 'binary', content.encode('base64'), column))
-
-class TestSelectionExport(TestBasicExport):
-    def test_selection(self):
-        [record] = self.Model.browse(self.cr, self.uid, [self.Model.create(self.cr, self.uid, {
-            'selection': 2,
-            'selection_str': 'C',
-        })])
-
-        column_name = 'selection'
-        column = self.get_column(column_name)
-        converter = self.registry('ir.qweb.field.selection')
-
-        value = converter.record_to_html(
-            self.cr, self.uid, column_name, record, column)
-        self.assertEqual(value, "réponse B")
-
-        column_name = 'selection_str'
-        column = self.get_column(column_name)
-        value = converter.record_to_html(
-            self.cr, self.uid, column_name, record, column)
-        self.assertEqual(value, "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?")
-
-class TestHTMLExport(TestBasicExport):
-    def test_html(self):
-        converter = self.get_converter('html')
-
-        input = '<span>span</span>'
-        value = converter(input)
-        self.assertEqual(value, input)
-
-class TestDatetimeExport(TestBasicExport):
-    def setUp(self):
-        super(TestDatetimeExport, self).setUp()
-        # set user tz to known value
-        Users = self.registry('res.users')
-        Users.write(self.cr, self.uid, self.uid, {
-            'tz': 'Pacific/Niue'
-        }, context=None)
-
-    def test_date(self):
-        converter = self.get_converter('date')
-
-        value = converter('2011-05-03')
-
-        # default lang/format is US
-        self.assertEqual(value, '05/03/2011')
-
-    def test_datetime(self):
-        converter = self.get_converter('datetime')
-
-        value = converter('2011-05-03 11:12:13')
-
-        # default lang/format is US
-        self.assertEqual(value, '05/03/2011 00:12:13')
-
-    def test_custom_format(self):
-        converter = self.get_converter('datetime')
-        converter2 = self.get_converter('date')
-        opts = {'format': 'MMMM d'}
-
-        value = converter('2011-03-02 11:12:13', options=opts)
-        value2 = converter2('2001-03-02', options=opts)
-        self.assertEqual(
-            value,
-            'March 2'
-        )
-        self.assertEqual(
-            value2,
-            'March 2'
-        )
-
-class TestDurationExport(TestBasicExport):
-    def setUp(self):
-        super(TestDurationExport, self).setUp()
-        # needs to have lang installed otherwise falls back on en_US
-        self.registry('res.lang').load_lang(self.cr, self.uid, 'fr_FR')
-
-    def test_negative(self):
-        converter = self.get_converter('float', 'duration')
-
-        with self.assertRaises(ValueError):
-            converter(-4)
-
-    def test_missing_unit(self):
-        converter = self.get_converter('float', 'duration')
-
-        with self.assertRaises(ValueError):
-            converter(4)
-
-    def test_basic(self):
-        converter = self.get_converter('float', 'duration')
-
-        result = converter(4, {'unit': 'hour'}, {'lang': 'fr_FR'})
-        self.assertEqual(result, u'4 heures')
-
-        result = converter(50, {'unit': 'second'}, {'lang': 'fr_FR'})
-        self.assertEqual(result, u'50 secondes')
-
-    def test_multiple(self):
-        converter = self.get_converter('float', 'duration')
-
-        result = converter(1.5, {'unit': 'hour'}, {'lang': 'fr_FR'})
-        self.assertEqual(result, u"1 heure 30 minutes")
-
-        result = converter(72, {'unit': 'second'}, {'lang': 'fr_FR'})
-        self.assertEqual(result, u"1 minute 12 secondes")
-
-class TestRelativeDatetime(TestBasicExport):
-    # not sure how a test based on "current time" should be tested. Even less
-    # so as it would mostly be a test of babel...
-
-    def setUp(self):
-        super(TestRelativeDatetime, self).setUp()
-        # needs to have lang installed otherwise falls back on en_US
-        self.registry('res.lang').load_lang(self.cr, self.uid, 'fr_FR')
-
-    def test_basic(self):
-        converter = self.get_converter('datetime', 'relative')
-        t = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
-
-        result = converter(t, context={'lang': 'fr_FR'})
-        self.assertEqual(result, u"il y a 1 heure")
diff --git a/openerp/tests/addons/test_converter/tests/test_vectors/image b/openerp/tests/addons/test_converter/tests/test_vectors/image
deleted file mode 100644 (file)
index 889c905..0000000
Binary files a/openerp/tests/addons/test_converter/tests/test_vectors/image and /dev/null differ
diff --git a/openerp/tests/addons/test_converter/tests/test_vectors/pdf b/openerp/tests/addons/test_converter/tests/test_vectors/pdf
deleted file mode 100644 (file)
index 6ae543d..0000000
Binary files a/openerp/tests/addons/test_converter/tests/test_vectors/pdf and /dev/null differ
diff --git a/openerp/tests/addons/test_converter/tests/test_vectors/pptx b/openerp/tests/addons/test_converter/tests/test_vectors/pptx
deleted file mode 100644 (file)
index f798aca..0000000
Binary files a/openerp/tests/addons/test_converter/tests/test_vectors/pptx and /dev/null differ
diff --git a/openerp/tests/addons/test_exceptions/__init__.py b/openerp/tests/addons/test_exceptions/__init__.py
deleted file mode 100644 (file)
index fe44871..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# -*- coding: utf-8 -*-
-import models
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_exceptions/__openerp__.py b/openerp/tests/addons/test_exceptions/__openerp__.py
deleted file mode 100644 (file)
index fc104f1..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-{
-    'name': 'test-exceptions',
-    'version': '0.1',
-    'category': 'Tests',
-    'description': """A module to generate exceptions.""",
-    'author': 'OpenERP SA',
-    'maintainer': 'OpenERP SA',
-    'website': 'http://www.openerp.com',
-    'depends': ['base'],
-    'data': ['view.xml', 'ir.model.access.csv'],
-    'installable': True,
-    'auto_install': False,
-}
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_exceptions/ir.model.access.csv b/openerp/tests/addons/test_exceptions/ir.model.access.csv
deleted file mode 100644 (file)
index 56f99ef..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
-access_test_exceptions_model,access_test_exceptions_model,model_test_exceptions_model,,1,1,1,1
diff --git a/openerp/tests/addons/test_exceptions/models.py b/openerp/tests/addons/test_exceptions/models.py
deleted file mode 100644 (file)
index a22c6ea..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-# -*- coding: utf-8 -*-
-import openerp
-
-class m(openerp.osv.osv.Model):
-    """ This model exposes a few methods that will raise the different
-        exceptions that must be handled by the server (and its RPC layer)
-        and the clients.
-    """
-    _name = 'test.exceptions.model'
-
-    def generate_except_osv(self, cr, uid, ids, context=None):
-        # title is ignored in the new (6.1) exceptions
-        raise openerp.osv.osv.except_osv('title', 'description')
-
-    def generate_except_orm(self, cr, uid, ids, context=None):
-        # title is ignored in the new (6.1) exceptions
-        raise openerp.osv.orm.except_orm('title', 'description')
-
-    def generate_warning(self, cr, uid, ids, context=None):
-        raise openerp.exceptions.Warning('description')
-
-    def generate_redirect_warning(self, cr, uid, ids, context=None):
-        dummy, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'test_exceptions', 'action_test_exceptions')
-        raise openerp.exceptions.RedirectWarning('description', action_id, 'go to the redirection')
-
-    def generate_access_denied(self, cr, uid, ids, context=None):
-        raise openerp.exceptions.AccessDenied()
-
-    def generate_access_error(self, cr, uid, ids, context=None):
-        raise openerp.exceptions.AccessError('description')
-
-    def generate_exc_access_denied(self, cr, uid, ids, context=None):
-        raise Exception('AccessDenied')
-
-    def generate_undefined(self, cr, uid, ids, context=None):
-        self.surely_undefined_symbol
-
-
-    def generate_except_osv_safe_eval(self, cr, uid, ids, context=None):
-        self.generate_safe_eval(cr, uid, ids, self.generate_except_osv, context)
-
-    def generate_except_orm_safe_eval(self, cr, uid, ids, context=None):
-        self.generate_safe_eval(cr, uid, ids, self.generate_except_orm, context)
-
-    def generate_warning_safe_eval(self, cr, uid, ids, context=None):
-        self.generate_safe_eval(cr, uid, ids, self.generate_warning, context)
-
-    def generate_redirect_warning_safe_eval(self, cr, uid, ids, context=None):
-        self.generate_safe_eval(cr, uid, ids, self.generate_redirect_warning, context)
-
-    def generate_access_denied_safe_eval(self, cr, uid, ids, context=None):
-        self.generate_safe_eval(cr, uid, ids, self.generate_access_denied, context)
-
-    def generate_access_error_safe_eval(self, cr, uid, ids, context=None):
-        self.generate_safe_eval(cr, uid, ids, self.generate_access_error, context)
-
-    def generate_exc_access_denied_safe_eval(self, cr, uid, ids, context=None):
-        self.generate_safe_eval(cr, uid, ids, self.generate_exc_access_denied, context)
-
-    def generate_undefined_safe_eval(self, cr, uid, ids, context=None):
-        self.generate_safe_eval(cr, uid, ids, self.generate_undefined, context)
-
-
-    def generate_safe_eval(self, cr, uid, ids, f, context):
-        globals_dict = { 'generate': lambda *args: f(cr, uid, ids, context) }
-        openerp.tools.safe_eval.safe_eval("generate()", mode='exec', globals_dict=globals_dict)
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_exceptions/view.xml b/openerp/tests/addons/test_exceptions/view.xml
deleted file mode 100644 (file)
index 4d8e820..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-    <data>
-
-        <record id="view_test_exceptions_model" model="ir.ui.view">
-            <field name="name">Test exceptions</field>
-            <field name="model">test.exceptions.model</field>
-            <field name="arch" type="xml">
-                <form string="Test exceptions">
-                    <label string="Each button generates a specific exception on the server. The text on the right is the expected representation of the exception when displayed on the client. Button marked with a '*' use safe_eval()."/>
-                    <separator string="" colspan="8"/>
-                    <group colspan="8" col="8">
-                    <group colspan="4" col="8">
-                      <group colspan="8" col="8">
-                          <button name="generate_except_osv" string="except_osv" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Warning-description"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_except_orm" string="except_orm" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Warning-description"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_warning" string="Warning" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Warning-description"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_redirect_warning" string="RedirectWarning" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Warning-description-redirection button"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_access_denied" string="AccessDenied" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Access denied-traceback"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_access_error" string="AccessError" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Access rights error-description"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_exc_access_denied" string="Exc AccessDenied" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Access denied-traceback"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_undefined" string="Undefined" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Server error-traceback"/>
-                      </group>
-                    </group>
-                    <group colspan="4" col="8">
-                      <group colspan="8" col="8">
-                          <button name="generate_except_osv_safe_eval" string="except_osv*" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Warning-description"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_except_orm_safe_eval" string="except_orm*" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Warning-description"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_warning_safe_eval" string="Warning*" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Warning-description"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_redirect_warning_safe_eval" string="RedirectWarning*" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Warning-description-redirection button"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_access_denied_safe_eval" string="AccessDenied*" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Access denied-traceback"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_access_error_safe_eval" string="AccessError*" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Access rights error-description"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_exc_access_denied_safe_eval" string="Exc AccessDenied*" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Access denied-traceback"/>
-                      </group>
-                      <group colspan="8" col="8">
-                          <button name="generate_undefined_safe_eval" string="Undefined*" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="Server error-traceback"/>
-                      </group>
-                    </group>
-                    </group>
-                </form>
-           </field>
-        </record>
-
-        <record id="action_test_exceptions" model="ir.actions.act_window">
-            <field name="name">Test exceptions</field>
-            <field name="type">ir.actions.act_window</field>
-            <field name="res_model">test.exceptions.model</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">form</field>
-            <field name="target">new</field>
-        </record>
-
-        <menuitem icon="STOCK_PREFERENCES" id="base.menu_tests" name="Tests" sequence="1000000"/>
-
-        <menuitem id="menu_test_exceptions" parent="base.menu_tests" name="Test exceptions"/>
-
-        <menuitem id="menu_test_exceptions_leaf"
-            name="Test exceptions"
-            action="action_test_exceptions"
-            parent="menu_test_exceptions"/>
-    </data>
-</openerp>
diff --git a/openerp/tests/addons/test_impex/__init__.py b/openerp/tests/addons/test_impex/__init__.py
deleted file mode 100644 (file)
index bff786c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-import models
diff --git a/openerp/tests/addons/test_impex/__openerp__.py b/openerp/tests/addons/test_impex/__openerp__.py
deleted file mode 100644 (file)
index 2b52b6a..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-{
-    'name': 'test-import-export',
-    'version': '0.1',
-    'category': 'Tests',
-    'description': """A module to test import/export.""",
-    'author': 'OpenERP SA',
-    'maintainer': 'OpenERP SA',
-    'website': 'http://www.openerp.com',
-    'depends': ['base'],
-    'data': ['ir.model.access.csv'],
-    'installable': True,
-    'auto_install': False,
-}
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_impex/ir.model.access.csv b/openerp/tests/addons/test_impex/ir.model.access.csv
deleted file mode 100644 (file)
index fd0f737..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
-access_export_boolean,access_export_boolean,model_export_boolean,,1,1,1,1
-access_export_integer,access_export_integer,model_export_integer,,1,1,1,1
-access_export_float,access_export_float,model_export_float,,1,1,1,1
-access_export_decimal,access_export_decimal,model_export_decimal,,1,1,1,1
-access_export_string_bounded,access_export_string_bounded,model_export_string_bounded,,1,1,1,1
-access_export_string_required,access_export_string_required,model_export_string_required,,1,1,1,1
-access_export_string,access_export_string,model_export_string,,1,1,1,1
-access_export_date,access_export_date,model_export_date,,1,1,1,1
-access_export_datetime,access_export_datetime,model_export_datetime,,1,1,1,1
-access_export_text,access_export_text,model_export_text,,1,1,1,1
-access_export_selection,access_export_selection,model_export_selection,,1,1,1,1
-access_export_selection_function,access_export_selection_function,model_export_selection_function,,1,1,1,1
-access_export_many2one,access_export_many2one,model_export_many2one,,1,1,1,1
-access_export_one2many,access_export_one2many,model_export_one2many,,1,1,1,1
-access_export_many2many,access_export_many2many,model_export_many2many,,1,1,1,1
-access_export_function,access_export_function,model_export_function,,1,1,1,1
-access_export_one2many_child,access_export_one2many_child,model_export_one2many_child,,1,1,1,1
-access_export_one2many_multiple,access_export_one2many_multiple,model_export_one2many_multiple,,1,1,1,1
-access_export_one2many_multiple_child,access_export_one2many_multiple_child,model_export_one2many_multiple_child,,1,1,1,1
-access_export_one2many_child_1,access_export_one2many_child_1,model_export_one2many_child_1,,1,1,1,1
-access_export_one2many_child_2,access_export_one2many_child_2,model_export_one2many_child_2,,1,1,1,1
-access_export_many2many_other,access_export_many2many_other,model_export_many2many_other,,1,1,1,1
-access_export_selection_withdefault,access_export_selection_withdefault,model_export_selection_withdefault,,1,1,1,1
-access_export_one2many_recursive,access_export_one2many_recursive,model_export_one2many_recursive,,1,1,1,1
-access_export_unique,access_export_unique,model_export_unique,,1,1,1,1
diff --git a/openerp/tests/addons/test_impex/models.py b/openerp/tests/addons/test_impex/models.py
deleted file mode 100644 (file)
index 8c76850..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-# -*- coding: utf-8 -*-
-from openerp.osv import orm, fields
-
-def selection_fn(obj, cr, uid, context=None):
-    return list(enumerate(["Corge", "Grault", "Wheee", "Moog"]))
-
-def function_fn(model, cr, uid, ids, field_name, arg, context):
-    return dict((id, 3) for id in ids)
-def function_fn_write(model, cr, uid, id, field_name, field_value, fnct_inv_arg, context):
-    """ just so CreatorCase.export can be used
-    """
-    pass
-
-models = [
-    ('boolean', fields.boolean()),
-    ('integer', fields.integer()),
-    ('float', fields.float()),
-    ('decimal', fields.float(digits=(16, 3))),
-    ('string.bounded', fields.char('unknown', size=16)),
-    ('string.required', fields.char('unknown', size=None, required=True)),
-    ('string', fields.char('unknown', size=None)),
-    ('date', fields.date()),
-    ('datetime', fields.datetime()),
-    ('text', fields.text()),
-    ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux"), (4, '')])),
-    ('selection.function', fields.selection(selection_fn)),
-    # just relate to an integer
-    ('many2one', fields.many2one('export.integer')),
-    ('one2many', fields.one2many('export.one2many.child', 'parent_id')),
-    ('many2many', fields.many2many('export.many2many.other')),
-    ('function', fields.function(function_fn, fnct_inv=function_fn_write, type="integer")),
-    # related: specialization of fields.function, should work the same way
-    # TODO: reference
-]
-for name, field in models:
-    attrs = {
-        '_name': 'export.%s' % name,
-        '_columns': {
-            'const': fields.integer(),
-            'value': field
-        },
-        '_defaults': {'const': 4},
-        'name_get': (lambda self, cr, uid, ids, context=None:
-            [(record.id, "%s:%s" % (self._name, record.value))
-             for record in self.browse(cr, uid, ids, context=context)]),
-        'name_search': (lambda self, cr, uid, name, operator, context=None:
-                self.name_get(cr, uid,
-                    self.search(cr, uid, [['value', operator, int(name.split(':')[1])]])
-                    , context=context)
-                if isinstance(name, basestring) and name.split(':')[0] == self._name
-                else [])
-    }
-    NewModel = type(
-        'Export%s' % ''.join(section.capitalize() for section in name.split('.')),
-        (orm.Model,),
-        attrs)
-
-class One2ManyChild(orm.Model):
-    _name = 'export.one2many.child'
-    # FIXME: orm.py:1161, fix to name_get on m2o field
-    _rec_name = 'value'
-
-    _columns = {
-        'parent_id': fields.many2one('export.one2many'),
-        'str': fields.char('unknown', size=None),
-        'value': fields.integer()
-    }
-    def name_get(self, cr, uid, ids, context=None):
-        return [(record.id, "%s:%s" % (self._name, record.value))
-            for record in self.browse(cr, uid, ids, context=context)]
-    def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
-        return (self.name_get(cr, user,
-                self.search(cr, user, [['value', operator, int(name.split(':')[1])]])
-                , context=context)
-            if isinstance(name, basestring) and name.split(':')[0] == self._name
-            else [])
-
-class One2ManyMultiple(orm.Model):
-    _name = 'export.one2many.multiple'
-
-    _columns = {
-        'parent_id': fields.many2one('export.one2many.recursive'),
-        'const': fields.integer(),
-        'child1': fields.one2many('export.one2many.child.1', 'parent_id'),
-        'child2': fields.one2many('export.one2many.child.2', 'parent_id'),
-    }
-    _defaults = { 'const': 36 }
-
-class One2ManyChildMultiple(orm.Model):
-    _name = 'export.one2many.multiple.child'
-    # FIXME: orm.py:1161, fix to name_get on m2o field
-    _rec_name = 'value'
-
-    _columns = {
-        'parent_id': fields.many2one('export.one2many.multiple'),
-        'str': fields.char('unknown', size=None),
-        'value': fields.integer()
-    }
-    def name_get(self, cr, uid, ids, context=None):
-        return [(record.id, "%s:%s" % (self._name, record.value))
-            for record in self.browse(cr, uid, ids, context=context)]
-class One2ManyChild1(orm.Model):
-    _name = 'export.one2many.child.1'
-    _inherit = 'export.one2many.multiple.child'
-class One2ManyChild2(orm.Model):
-    _name = 'export.one2many.child.2'
-    _inherit = 'export.one2many.multiple.child'
-
-class Many2ManyChild(orm.Model):
-    _name = 'export.many2many.other'
-    # FIXME: orm.py:1161, fix to name_get on m2o field
-    _rec_name = 'value'
-
-    _columns = {
-        'str': fields.char('unknown', size=None),
-        'value': fields.integer()
-    }
-    def name_get(self, cr, uid, ids, context=None):
-        return [(record.id, "%s:%s" % (self._name, record.value))
-            for record in self.browse(cr, uid, ids, context=context)]
-    def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
-        return (self.name_get(cr, user,
-                    self.search(cr, user, [['value', operator, int(name.split(':')[1])]])
-                    , context=context)
-                if isinstance(name, basestring) and name.split(':')[0] == self._name
-                else [])
-
-class SelectionWithDefault(orm.Model):
-    _name = 'export.selection.withdefault'
-
-    _columns = {
-        'const': fields.integer(),
-        'value': fields.selection([(1, "Foo"), (2, "Bar")]),
-    }
-    _defaults = {
-        'const': 4,
-        'value': 2,
-    }
-
-class RecO2M(orm.Model):
-    _name = 'export.one2many.recursive'
-
-    _columns = {
-        'value': fields.integer(),
-        'child': fields.one2many('export.one2many.multiple', 'parent_id')
-    }
-
-class OnlyOne(orm.Model):
-    _name = 'export.unique'
-
-    _columns = {
-        'value': fields.integer(),
-    }
-    _sql_constraints = [
-        ('value_unique', 'unique (value)', "The value must be unique"),
-    ]
diff --git a/openerp/tests/addons/test_impex/tests/__init__.py b/openerp/tests/addons/test_impex/tests/__init__.py
deleted file mode 100644 (file)
index 8cab56f..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from . import test_export, test_import, test_load
-
-fast_suite = [
-]
-
-checks = [
-    test_export,
-    test_import,
-    test_load,
-]
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_impex/tests/contacts.json b/openerp/tests/addons/test_impex/tests/contacts.json
deleted file mode 100644 (file)
index 4f06503..0000000
+++ /dev/null
@@ -1 +0,0 @@
-[["Wood y Wood Pecker", "", "Snow Street, 25", "Kainuu", "Finland", "Supplier", "1", "0", "1", ""], ["Roger Pecker", "Default", "Snow Street, 27", "Kainuu", "Finland", "Supplier", "1", "0", "0", "Wood y Wood Pecker"], ["Sharon Pecker", "Shipping", "Snow Street, 28", "Kainuu", "Finland", "Supplier", "1", "0", "0", "Wood y Wood Pecker"], ["Thomas Pecker", "Contact", "Snow Street, 27", "Kainuu", "Finland", "Supplier", "1", "0", "0", "Wood y Wood Pecker"], ["Norseman Roundabout", "", "Atonium Street, 45a", "Brussels", "Belgium", "Supplier", "1", "0", "1", ""], ["Yvan Holiday", "Invoice", "Atonium Street, 45b", "Brussels", "Belgium", "Supplier", "1", "0", "0", "Norseman Roundabout"], ["Jack Unsworth", "Contact", "Atonium Street, 45a", "Brussels", "Belgium", "Supplier", "1", "0", "0", "Norseman Roundabout"]]
diff --git a/openerp/tests/addons/test_impex/tests/contacts_big.json b/openerp/tests/addons/test_impex/tests/contacts_big.json
deleted file mode 100644 (file)
index 5dfe77e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-[["88117cfa", "", "aa48ae52@df356352.com", ""], ["54455583", "0770708969", "75ce07b4@8c061041.com", ""], ["a4f9804e", "0794216989", "6a994598@402c461b.com", ""], ["0cfd7c89", "0788771669", "f2598da7@15f7036b.com", ""], ["220bfe63", "0767248573", "30d7e3f3@993ab043.com", ""], ["33bc0e31", "0747111853", "e86b7718@36a11f08.com", ""], ["ad3d1c39", "0707461083", "17ddff88@de2d370e.com", ""], ["06ba223e", "0793743830", "6c77d328@247685b0.com", ""], ["d457d407", "0723226290", "f497c7a2@c39adaaf.com", ""], ["b24f01d4", "0785727376", "e5c03c2c@be7b89f7.com", ""], ["666bbc2f", "0726730949", "1fbcb995@6b02d029.com", ""], ["8e29a5f9", "0769331825", "17771bb9@4e36debc.com", ""], ["330ee602", "0760467697", "86af9f82@bd3cdfba.com", ""], ["bcd48752", "0747166758", "ccc4c255@05124d59.com", ""], ["4a16c054", "0776729581", "c87cc075@6d84e9ae.com", ""], ["c065ff62", "0708945599", "41391787@d4c3369a.com", ""], ["8636e303", "0798878005", "520e2cee@3506cf0e.com", ""], ["0bdecf04", "0766303691", "7fb38fdd@46f90ec4.com", ""], ["e1ac6628", "0797335906", "5483d5cf@3372b5ea.com", ""], ["f0a24042", "0774112437", "4e99fd6b@7f727830.com", ""], ["41f18eb6", "0757100173", "f904520a@4d42ffde.com", ""], ["c4a8cde7", "", "aa344d15@3dd9bb6f.com", ""], ["abd14d72", "0778342908", "ed9cb7e3@c6fdb61c.com", ""], ["44c65e49", "0711762677", "ef6952c5@a9cd6711.com", ""], ["a3238267", "0758374338", "203081cd@e1263e29.com", ""], ["412069c4", "0745736692", "f2b989ed@67ac8c23.com", ""], ["b332577b", "0783480568", "4382789d@e1dd2623.com", ""], ["84fccf60", "", "ec2987d5@d777ea94.com", ""], ["c633db24", "0780843363", "9c6a9a39@3ba1bc58.com", ""], ["833bdb8f", "", "0c53568d@170c6826.com", ""], ["c6496a37", "0702644587", "808dc444@d3c87550.com", ""], ["3a9cf735", "0732254904", "825cb1f5@9599a25c.com", ""], ["d3bd7567", "0750010894", "3dd53f94@381f30ff.com", ""], ["ec9d38fa", "0722807920", "a63d63c9@9fc07187.com", ""], ["9e858edf", "0759964893", "ee8fc817@4969b884.com", ""], ["3820033b", "0713040363", "846b31dd@1fde871f.com", ""], ["78e3f6fa", "0713215706", "3a1bd069@a2165e02.com", ""], ["833bf332", "0732488435", "e962f524@7a67d172.com", ""], ["ebe0d200", "0791879047", "34a1610c@e30a9051.com", ""], ["e27d3d81", "0702888121", "a6c52338@56630271.com", ""], ["10423400", "0775217021", "24c5bd9d@3b4bbb75.com", ""], ["923f52a7", "0747670735", "19ee7b6b@fb8077b5.com", ""], ["fe50aca6", "0785899752", "99b1c2e4@30f03fdd.com", ""], ["bdbff3f2", "", "cc5cf567@c13f1531.com", ""], ["6dbbeeb9", "0755330628", "33efb58d@1ac1966a.com", ""], ["f88681ce", "", "4d01aa9a@461b4345.com", ""], ["8ff958c9", "0728877017", "c7327902@eae323df.com", ""], ["d8651703", "0711929482", "6aed493b@44b94bb2.com", ""], ["fcf4863f", "0721975645", "c8171bbf@2cefedab.com", ""], ["614949ae", "0708626227", "c54a3aca@bc635fa8.com", ""], ["7b0767ab", "0773868355", "2e5e7b82@bde16366.com", ""], ["ea0fb610", "0714907110", "3781b5b3@6ade96c7.com", ""], ["1833ee8b", "0754159809", "a1fd1724@0a180cd8.com", ""], ["277ea017", "0756815282", "ad93976a@022ff737.com", ""], ["67e662d9", "0789105520", "ecb672e5@df0e020e.com", ""], ["18f2288c", "", "25de55ef@a03da5ef.com", ""], ["32dcbf86", "0738646453", "88e37cac@ec308293.com", ""], ["242eb33b", "0702264598", "d132623d@6a8e65f4.com", ""], ["f2f8e50a", "0731234118", "10bdb9d0@a1dec378.com", ""], ["c414145d", "0723184859", "465e6323@083bdb6f.com", ""], ["1a6d51be", "0782912489", "7f69541f@5e17335f.com", ""], ["ef35d7bc", "0705243855", "0a8f6885@8a236f11.com", ""], ["ac48d3c2", "0714165417", "aa9c4199@1ab6a42c.com", ""], ["5ba7effd", "0731285368", "e7b91cfb@18ab2ca6.com", ""], ["c8349e83", "0711248806", "bf08adeb@72d14356.com", ""], ["ccb97edd", "", "b64dc525@fff1e0b6.com", ""], ["f6b196e8", "0754457697", "915a9cb0@75e21eed.com", ""], ["7a22e438", "0750066621", "4c2ba953@96df5597.com", ""], ["ffb4ecaf", "0791152499", "d9159dc2@6bdedc89.com", ""], ["64d655fb", "0756407008", "498110df@dfc1445a.com", ""], ["f27b441d", "0734603187", "4a23bbf1@ef4d212f.com", ""], ["2405cb10", "0761979851", "92b8250a@125bbd04.com", ""], ["fce58db4", "", "f6dfbd77@704c66d0.com", ""], ["2b16a16b", "0715217717", "d22db762@46605453.com", ""], ["4fbb5479", "0788391407", "e41e70f2@c575eeed.com", ""], ["5929e7ad", "0730836883", "38a50e2f@0cbd9fc0.com", ""], ["9fab30be", "0758240568", "f8422724@eef7fb6f.com", ""], ["57161fa2", "", "913ed6fb@1f9a591c.com", ""], ["787471f3", "0729709798", "45ed74ea@4209730d.com", ""], ["fce02cc8", "0717080949", "0430d0fa@09917e4b.com", ""], ["e5745b08", "", "2e337c3a@038a6c53.com", ""], ["bba2eeb4", "0754696079", "37c75466@05cbd7d1.com", ""], ["f072a1b0", "0774261408", "605c6350@5a7cc516.com", ""], ["95d2b9c0", "", "8935e406@c23ff41c.com", ""], ["8ac4ea3a", "0714072646", "734cfea9@fb198808.com", ""], ["5a523460", "", "2be9f139@0cf11f87.com", ""], ["5b10ec25", "0732185274", "098c0cf3@3676b22d.com", ""], ["8aa42976", "", "2942960c@1ae8ae04.com", ""], ["cdb88ecb", "0758961947", "0ea31305@77af1e9a.com", ""], ["0bd54356", "0784831852", "c437852e@c8e319ee.com", ""], ["1d1768af", "0793590304", "d66c02aa@649e09d1.com", ""], ["c19a3d43", "0749800307", "cf8fc137@761da752.com", ""], ["9c81b4c9", "", "ab84e443@09f1f4ea.com", ""], ["8811594d", "", "53698008@bcc6d45a.com", ""], ["97a8c2d1", "0715236942", "cf88f0ab@fa3f7a28.com", ""], ["fc7d6b5f", "0770870220", "b0e2ee90@41460794.com", ""], ["a158270a", "", "aaab6cf4@d31f8c47.com", ""], ["20bef3ca", "0744837638", "fca8c63c@8dae6b0a.com", ""], ["424b3e69", "", "7d2bb9c4@856ce530.com", ""], ["8ecc1d0a", "", "6df56a62@deac5fbf.com", ""], ["7e1f1b8c", "0795180887", "18d2a24d@a931a1cb.com", ""], ["4546b364", "0771517872", "77ac1ab3@3cd24c25.com", ""], ["a44b23b2", "", "55e75fcb@e20b079d.com", ""], ["90c52454", "0713222790", "e83be6ad@faaa1dcc.com", ""], ["f5fe091e", "0740461816", "fc4b6360@c84d9776.com", ""], ["01052d30", "0790737321", "421a7451@c44558f7.com", ""], ["a65634f3", "0733738433", "a7d57f5f@4f0007cc.com", ""], ["960aae3c", "", "3e2338e3@97fa70e8.com", ""], ["590858c2", "0736114828", "59cec23c@fb9704f4.com", ""], ["75725ca0", "0777217002", "5c7b484a@8c386315.com", ""], ["a16cf653", "0732018015", "a105f736@87a6c14b.com", ""], ["1ee991c1", "0759093690", "1cc3c001@d70dae0e.com", ""], ["48434804", "", "d3cee127@7dbf4217.com", ""], ["edfd871e", "0757718444", "d63d5c26@81eaeb47.com", ""], ["4fcc9a1c", "0738617045", "b114a376@9bd5a931.com", ""], ["b2001895", "0782251612", "a8868ffb@8114353b.com", ""], ["d61261f8", "0717555195", "ac613ffe@d4ee0d8c.com", ""], ["6327118e", "0739171459", "e047cf55@4920c1d3.com", ""], ["f6b14b7a", "0737868032", "418ed43d@b549551d.com", ""], ["22bc0aa9", "0759663409", "90494f38@f50ab03d.com", ""], ["3145a855", "0788697773", "8a0870bd@744b0c6b.com", ""], ["72f043d6", "0780961318", "859d6c8c@d6a29fe3.com", ""], ["98544981", "0707687449", "53a75fab@6ad934ef.com", ""], ["535ddbe3", "0751302080", "5d5dbab7@9b651502.com", ""], ["c3e5f1ac", "", "62955f00@74d50e7c.com", ""], ["6470f36e", "0776617461", "5f6d81a4@e535baa8.com", ""], ["ed39f4ae", "0766428270", "c73a731a@186ebf0c.com", ""], ["6aa1b596", "0720348914", "67d7e38b@5798f6cc.com", ""], ["f5a778ee", "0773231447", "5919b978@2768de0c.com", ""], ["9d530c2b", "0776812615", "4609c18c@aeca697d.com", ""], ["89a13571", "0744825918", "766ddb77@7dfc129d.com", ""], ["90dd9419", "0722654005", "22bef5d1@74801d84.com", ""], ["0d69e61d", "0779495819", "fa2fce6e@d3b9debf.com", ""], ["0a64ee31", "0741511488", "f48455f9@0163e8a5.com", ""], ["87f3e7fd", "0792428108", "a7763cb9@ae893b19.com", ""], ["99a66411", "0785175788", "3abf4513@c90ddc54.com", ""], ["d4d56088", "0748950722", "2dfc3b17@4af80b7c.com", ""], ["1f001808", "0733396761", "59ae4761@17a1bad6.com", ""], ["2d5cac56", "0744393937", "709feac1@1c16f3b0.com", ""], ["d0acb4ea", "0770269511", "66221771@3ff30570.com", ""], ["4f57869d", "0706718397", "1cd2ed99@0ba6e1d3.com", ""], ["a2e66c44", "", "753da3f0@41f8ab02.com", ""], ["843dc71f", "0757529013", "016ff59d@6c1f9655.com", ""], ["c314a28e", "0767460679", "cd2ff99c@8b178d14.com", ""], ["f0d6631d", "0719558518", "0725dfc6@8ce4d6ff.com", ""], ["aa522bd8", "0735252785", "57d3edd6@5eb1bc66.com", ""], ["3f69f804", "0717000930", "6ca23f02@d169a50e.com", ""], ["cc98acfa", "0796356276", "19579c3b@39e71005.com", ""], ["9e6dbdc2", "0769660138", "6349577f@fba33845.com", ""], ["fba0618b", "0758705886", "ae7a9890@e776386f.com", ""], ["f36d563f", "0744525165", "c76184f9@5dccdf5b.com", ""], ["d2df6db1", "0783917910", "f7726c1a@c9e9b839.com", ""], ["8645707d", "0724081473", "842f2369@be456fbb.com", ""], ["95b4296a", "0782845149", "af879705@e8e7bd93.com", ""], ["345ba50c", "0792384168", "ca3ccb91@faf01bf8.com", ""], ["e05de824", "0707660173", "79f09a3d@3deafa86.com", ""], ["de837ee8", "0772243198", "b25275dc@f057ff78.com", ""], ["ae9f1402", "0704963747", "253d5995@6c771579.com", ""], ["259c5532", "0781521724", "cec33738@874afdb1.com", ""], ["1bc82378", "0748931231", "7b3feccd@cd205def.com", ""], ["c73869a0", "0728684210", "790c4825@22921089.com", ""], ["725a20ee", "0716516714", "a5852a47@85846987.com", ""], ["f975f39e", "0732891142", "aea8acea@a6feaa85.com", ""], ["d0fbcd0b", "0753684058", "78f9d7f2@49082702.com", ""], ["d41da511", "0765105657", "bca42072@50aa0f9d.com", ""], ["42be8058", "0776465871", "15d0f988@805b0cb0.com", ""], ["269eda62", "0705841239", "67dc4d59@51389f5b.com", ""], ["2ffc5c2c", "0732437371", "c3b76c4c@9772ea3b.com", ""], ["258e1957", "0768636603", "476831de@98414c35.com", ""], ["a18a68f6", "", "136d0b0e@15864440.com", ""], ["56b9197d", "0734391703", "ab36de76@d7a2bc96.com", ""], ["e1acdbc5", "0790910000", "867827b4@fdffc111.com", ""], ["21e48ef2", "", "4a1bbb5c@29b1ac3a.com", ""], ["aec1a0ea", "0749338116", "64dbedc8@6e81a330.com", ""], ["b1afbf53", "0701600188", "5f1e3fcc@0c6cba23.com", ""], ["bc7e214f", "0725934639", "369caec7@ba1df297.com", ""], ["dd059d1e", "0774510615", "bac60ace@a354f53c.com", ""], ["cbe822f4", "0745859979", "01bebc6e@735bdad1.com", ""], ["961d21e8", "0741358098", "9348702e@ce9c1230.com", ""], ["c8ae2583", "", "3682409d@a15621c4.com", ""], ["c326d443", "0784986501", "0a505045@96938424.com", ""], ["3e51bb13", "0764438578", "b713c235@8e350663.com", ""], ["ad38c924", "0772825196", "ce8d0f19@b2d4ad9f.com", ""], ["65bd5021", "", "3f6318d5@91f006ed.com", ""], ["efd2705b", "0770828242", "736fe829@7c30bc29.com", ""], ["6ebdfef1", "0710559084", "e035b963@a163fb9d.com", ""], ["021358ef", "0711112569", "ecc20e51@547a7cc2.com", ""], ["548d9a59", "0790101525", "d42aa9de@6f91f22e.com", ""], ["262f96db", "0780153799", "094258c8@e03d9795.com", ""], ["a548913a", "0779869470", "6a6999ca@ca0a88d1.com", ""], ["e7beec03", "0796588859", "5e9940fa@eca859cc.com", ""], ["ae5a8077", "", "5ab3a073@18a45934.com", ""], ["eae22551", "0747781534", "6b04366d@7653fc99.com", ""], ["2fb9da57", "0799726898", "895646f6@2a66fe2e.com", ""], ["51d83f2f", "0704387855", "0d2ea467@62feebfc.com", ""], ["3da4bc6d", "0786428049", "db6a479a@37899df4.com", ""], ["c08101a4", "0751956133", "67852281@0bb68d55.com", ""], ["b5f8c156", "0754878556", "bad4eea7@4d5a817a.com", ""], ["ded80b77", "0772054646", "ebb2cd45@54262aec.com", ""], ["7b086cfd", "0776426442", "3b503434@d46b44fe.com", ""], ["7a64e1e8", "0740165572", "e9d50a03@38375fce.com", ""], ["ceaa584b", "0791796518", "b5839977@cfe3ae10.com", ""], ["9c4a8683", "0790187277", "f8e4fe02@02fc1038.com", ""], ["193f99b7", "", "915c1574@50758bc3.com", ""], ["6fb6c30a", "0720403051", "f4bcd3ca@a8ab8216.com", ""], ["9486800f", "0723518103", "a3886d22@6600cdda.com", ""], ["6e09f6bd", "0707095889", "868ffc60@46eb3135.com", ""], ["da1bd13c", "0756382253", "f34abc2c@d18ec367.com", ""], ["3910ae7d", "0786894789", "a8a751a5@9ddf61c7.com", ""], ["32e5be0c", "0738208660", "eab918a8@aeb7a8fe.com", ""], ["c6582498", "0744023415", "b893479d@9ad5c44f.com", ""], ["0c59f520", "", "d552d0fe@8675edcc.com", ""], ["3458ee7a", "", "c1145be6@4043406c.com", ""], ["91fad8e7", "0701771702", "a13640e5@2370e9a4.com", ""], ["2230118a", "0792290165", "529b9444@17c21068.com", ""], ["2df4bff7", "0783219548", "99a9af11@8e5a539d.com", ""], ["0a752397", "0723553802", "b094b6c9@2d8fb162.com", ""], ["918f92a0", "0790485640", "3984cea2@0750289f.com", ""], ["ff811166", "0756833896", "960e71f3@6ded22f4.com", ""], ["6578cd85", "0791455734", "f7e56f2a@35a68ac6.com", ""], ["fd21c33b", "0794847916", "23ae4c63@51515e19.com", ""], ["a12e519d", "0700174532", "d3b4264d@0c9d62f1.com", ""], ["96827dcd", "0780494940", "d70d9ab7@df66a83c.com", ""], ["8cd46009", "", "a8addc7c@c899c5f7.com", ""], ["982fb2ee", "0778368795", "b99de484@d262779b.com", ""], ["dbe5ea51", "0756105541", "ad90b51c@3f1af6d8.com", ""], ["3d963533", "0761709058", "85b32c2c@d99341a7.com", ""], ["064f2df0", "0791023088", "45d63e8f@ce0852ad.com", ""], ["e93edb9d", "0713294122", "b9b82603@a226aaba.com", ""], ["a94ada15", "0796377896", "5dd00c2d@794bcc18.com", ""], ["7de0f3db", "0748976042", "7714652c@3ef8b9d5.com", ""], ["6ff485d2", "0774873001", "77b5b11c@675bf1f7.com", ""], ["e472c175", "0761539211", "dc30792d@00a0ef22.com", ""], ["7a467ead", "0734394099", "e8d5a021@93926f82.com", ""], ["87749969", "0780899950", "dfe454aa@94a8fd44.com", ""], ["c24c8b06", "0777380273", "98dc8782@26872819.com", ""], ["a059abab", "0753758924", "581281a2@890e7bc6.com", ""], ["3b85bdd1", "0784193150", "cf3c82aa@e5df6c1b.com", ""], ["7cbc188d", "0704027570", "c6444b77@8ab56b47.com", ""], ["12ff649b", "0755744514", "a7ce16ba@c054924a.com", ""], ["cc77e0c9", "0724057991", "4e2473d1@4aca6e0e.com", ""], ["026e42e1", "0741456583", "09906c46@e32bed7b.com", ""], ["9e6550e3", "0740725000", "27ddd3ff@c0891335.com", ""], ["700be741", "0715655544", "bcc93b4c@de8854d5.com", ""], ["f15ce066", "0770746966", "aff2e8a9@19f37ff3.com", ""], ["d56faa79", "0780216109", "7119d43e@10e506f6.com", ""], ["d0d97e5c", "0799577373", "9c131514@a5f6fdee.com", ""], ["d3320f24", "0789091392", "e9b8d4a6@e2c9fa38.com", ""], ["c9b3e28b", "0753027473", "d4c85b77@26952326.com", ""], ["5f81787f", "0789741665", "ca7dbbcc@97fc237d.com", ""], ["dd720b39", "0799154501", "9cc0df66@70c2f5d7.com", ""], ["f584f902", "", "38a024f0@27413ff3.com", ""], ["bb37782d", "0788379130", "01ef864a@191fed62.com", ""], ["f6964d05", "0732597248", "32d25b28@6ae6e905.com", ""], ["cfdc9a87", "", "f453b3f6@4ccf20a3.com", ""], ["2c6f8592", "0773589537", "d71824f7@91dd37aa.com", ""], ["ba101360", "0748932503", "674abbfc@81b3b946.com", ""], ["3b35863b", "0759392115", "c249bcc4@e650abe8.com", ""], ["610da7a5", "0712310446", "f4a25391@d7759c5f.com", ""], ["1557e96a", "0796582698", "4aaf8b75@99c557d5.com", ""], ["98519b81", "", "a5050e55@917cd419.com", ""], ["ed820127", "0754946950", "dd82c2c1@7b1ab977.com", ""], ["b78147ee", "0765494006", "53795132@dd38aec7.com", ""], ["3d4859e8", "0782874657", "67b89627@d2269d32.com", ""], ["ce2366c6", "0719814269", "c6200859@0788dca5.com", ""], ["fba51219", "", "b1d107dd@b664bba3.com", ""], ["7469e033", "0742202666", "9cbedb4b@31ba3795.com", ""], ["20c151e6", "0746146553", "90851479@e8a420a4.com", ""], ["14499acc", "0731779256", "c5c930ab@8de927e9.com", ""], ["71297ace", "0771197563", "1bf4547d@f5c8f5b3.com", ""], ["ea630ed8", "0712073290", "245f14c9@9470fedc.com", ""], ["2c54ae4c", "0767896178", "76f988c7@07ade1eb.com", ""], ["e143bc1c", "0798189886", "68717fc3@c4ca3101.com", ""], ["1626281f", "0782558075", "d87d65f6@585b48b9.com", ""], ["3df7b85d", "", "4ef416c6@0e39f805.com", ""], ["101c2c3a", "0754160899", "c77a5ff2@e3dbfd0f.com", ""], ["0e433acb", "", "ce18b2e0@18a803fe.com", ""], ["b454958d", "0758649506", "daadf940@35a7b0e0.com", ""], ["54dd80e3", "", "499da17c@7a6a8e16.com", ""], ["c26e8c0b", "0705458229", "816d4c62@f196940f.com", ""], ["d6c7c2fb", "0712740449", "eb0f3861@028419f0.com", ""], ["ca236785", "0710117939", "74d89d0f@9681f6e2.com", ""], ["03e89273", "0770985481", "a767a13e@202e1c7e.com", ""], ["df528ea6", "0715236243", "4451e8a7@ab9e9cfc.com", ""], ["b369c3a1", "0720792641", "af8e60fb@affbf045.com", ""], ["0e3b3dcb", "0726931283", "b6c80dd6@377de27c.com", ""], ["72f56e12", "0749900046", "dfd0d53e@a04c2e07.com", ""], ["0b616a06", "0788777560", "e520ec34@5a88ce36.com", ""], ["7dcd72da", "0727744886", "c7ecb909@efc93b54.com", ""], ["b6749523", "0755931358", "76b2e187@777e9047.com", ""], ["349f347f", "0772053293", "aed364ad@8f6c1755.com", ""], ["b0a24d58", "0737012558", "b013a7fd@a6d27266.com", ""], ["924dcf44", "0761967844", "539d93ff@4e1741bd.com", ""], ["90781369", "0745086457", "bae1c94b@df7075b3.com", ""], ["7bbf6e08", "0762583195", "c54b4a41@f08e61ec.com", ""], ["696d9ce3", "0753759660", "9be913a2@efd894d9.com", ""], ["32dea399", "0730020519", "b76d0594@9127aaf5.com", ""], ["4aada6ab", "0716703652", "1172d8f6@70029987.com", ""], ["c2b450aa", "0786756810", "05fa85d3@6305e8e2.com", ""], ["f7220cb8", "0776056046", "b14c6af3@ebfcf7d3.com", ""], ["7da05a35", "0741271883", "abe23d1a@35fc89a5.com", ""], ["b277cd48", "0703025387", "ed28c019@feb1fe78.com", ""], ["55a16321", "0740030335", "a3db681b@de92450b.com", ""], ["646ff9aa", "0777457443", "ea0704a8@d6c557b9.com", ""], ["557c863d", "0752298537", "c412a0a1@4db33621.com", ""], ["324da6d3", "", "61e7386a@d4a90843.com", ""], ["144835ab", "", "70744ba9@000ee58a.com", ""], ["eda25157", "0734918749", "efdbfb3e@83133e46.com", ""], ["bf5d4b09", "0712427566", "4b13f413@18c93b1c.com", ""], ["c3745e8a", "0723463789", "e5ccf0c5@00118e4a.com", ""], ["11c0a9da", "0778743010", "865392aa@1c8da31d.com", ""], ["7d0eac7d", "0701997984", "f2990151@e8d055f3.com", ""], ["e701488a", "0712599226", "f0d54316@037ea274.com", ""], ["acac2543", "0771603428", "73cc7388@981dc1a0.com", ""], ["82f371b0", "0758909949", "6ebe0478@2b14af27.com", ""], ["896bb3a8", "", "83158633@aa3fe414.com", ""], ["9ce45cf4", "", "38772abe@078a889f.com", ""], ["fcb5a33a", "0769530741", "29a4e798@effcb6ef.com", ""], ["116d4f92", "0786255405", "e78e003a@cf7ebbc9.com", ""], ["35e5bd6c", "0700694228", "399e4cd6@eb7a9b08.com", ""], ["71df9408", "0798109987", "91433ee1@b3240820.com", ""], ["294e2aa4", "0700947677", "abca0340@4cd61d52.com", ""], ["9553d892", "", "3af85bbd@90ee53d6.com", ""], ["bbcdb933", "0773940915", "de196282@bf4432fe.com", ""], ["3ccbc198", "0742527356", "014618cd@78b4016f.com", ""], ["100d9496", "0786481835", "df9a72cc@123af588.com", ""], ["57cfe177", "0789499193", "f5f0de36@27d22e23.com", ""], ["cdffca88", "0714652673", "4102e3e2@26a0d112.com", ""], ["69c5f300", "0762631167", "14f5940e@fe984955.com", ""], ["819e988a", "0798148053", "b78ed8c4@ef4a213d.com", ""], ["f5c6a92a", "0774167414", "c996df73@8a8a0586.com", ""], ["3d3f9cc9", "0766973558", "f0a43735@47580cbd.com", ""], ["a30056ab", "0787961025", "04256d2d@459d56d8.com", ""], ["7dc0d98e", "0741838356", "2a597e04@f7089dd3.com", ""], ["e19f381b", "", "ce9c237b@4ec0fd84.com", ""], ["34b3c355", "", "77bcc11b@2e791c9a.com", ""], ["4bca2052", "0799612575", "dba5fc2f@600d7b82.com", ""], ["dbc71748", "0726348854", "efc05aff@df67c9ee.com", ""], ["6fee2352", "0786598805", "c8bad10b@f550bfa0.com", ""], ["b56c589c", "0722452846", "43e2ed1c@4e233028.com", ""], ["77f4b627", "0795147567", "6f26728c@015d1c78.com", ""], ["e6e1b439", "0720916434", "6cf4f2cd@d0daef5a.com", ""], ["c4e49a4c", "0787210516", "bfc6dbf7@8f315a9f.com", ""], ["3f717509", "0704228943", "7a9fd98d@112a27be.com", ""], ["8c0f2abf", "0739504671", "b2c6a55d@bf7bb207.com", ""], ["d0ec34db", "0745702321", "4a426510@7c7cc4ba.com", ""], ["cfe0f395", "0708421594", "df815b04@d27562ee.com", ""], ["4ab6a603", "0781614226", "8e7498c4@216eb70c.com", ""], ["53727fa1", "0778595217", "0fef75b6@7e3eee41.com", ""], ["1dd3c6c9", "0795888769", "c88ef6ef@9182a8c1.com", ""], ["5dbdf1c2", "0700853188", "8b2128ff@5a2fa2e4.com", ""], ["5cbf9768", "0752068105", "064d2e97@f4631505.com", ""], ["b81ffbce", "0737952148", "ef90cb7e@7f7bb2d3.com", ""], ["6c9426b4", "0765528647", "571533da@097b853a.com", ""], ["76b87a42", "0735376294", "4fab1c8a@1004c035.com", ""], ["cc67d10f", "", "df05e09c@10cdee9b.com", ""], ["91f2c079", "0727155396", "f70f99d0@e56d0e68.com", ""], ["7687c589", "0723819703", "f4a37237@8306ec54.com", ""], ["cc065203", "0777023625", "235ab1ff@ad59efef.com", ""], ["ada410b8", "0795063396", "d58735f0@1cfd2246.com", ""], ["b17fda79", "0761763470", "06024431@3a90405d.com", ""], ["82c9cf46", "0795927937", "cc7d157e@fe591e61.com", ""], ["1229f23f", "0766794577", "196fbca7@2762695d.com", ""], ["41ee3436", "0751875418", "e789384d@1e8c078b.com", ""], ["b59b1ae3", "0706674618", "eaa7394a@9ca599d5.com", ""], ["32a1572d", "0739902105", "076df342@f7257d5d.com", ""], ["87142aaf", "0773843071", "2d1ed5d0@745442e0.com", ""], ["fd302180", "0752363053", "69d00e9f@4361b9c7.com", ""], ["9b129111", "0711208227", "e3f41ba4@230ff3c7.com", ""], ["00c627e0", "0795414341", "878530d9@891a7047.com", ""], ["f9bd3b66", "0754134870", "36b72937@8fe2912f.com", ""], ["d89c11eb", "0760560969", "6ec530b9@d7f30799.com", ""], ["e2b6ef2f", "0711238958", "6250bb8e@23e30295.com", ""], ["811c4293", "", "81aeac97@4083c108.com", ""], ["96c90ed7", "", "19727897@698b879b.com", ""], ["b82a09af", "", "7527ccbc@4e351f9e.com", ""], ["17e61a75", "", "ffdd4947@541dcc9f.com", ""], ["f97fa25d", "0749784894", "8b419430@4ae116d0.com", ""], ["13b9a7da", "0748292813", "1d2e437d@1f7fd137.com", ""], ["e0879369", "0778630426", "fbc7e460@350c1a55.com", ""], ["1a74805d", "0749531731", "f06e561d@ebbec1ed.com", ""], ["eb5db9c6", "0707095397", "ec71e260@1ab509e1.com", ""], ["9da2dc8e", "0717471756", "23418425@2dffbc70.com", ""], ["a6f6f46f", "0703721541", "037414ed@c1759536.com", ""], ["765a5633", "", "4fd61a7f@e9fc7672.com", ""], ["3a717722", "0785655203", "82f8e536@4aab3d98.com", ""], ["34b938b7", "0712389450", "0ce3ed1c@8d5aec4e.com", ""], ["fa4eaa80", "0760264228", "487d193e@86c36a2b.com", ""], ["6ba54ee3", "0737464908", "ee4791dc@6e7e18cc.com", ""], ["3d02d6e8", "", "b2c13ef3@66a3948e.com", ""], ["ee3c7b2e", "", "2c51c4fc@97eaabe3.com", ""], ["943acb8a", "0775102341", "306ebc20@ad6a778a.com", ""], ["e86c14c3", "0705424432", "40c1518d@76aab128.com", ""], ["0d993e76", "0729633289", "a18fcc70@f04ad1c7.com", ""], ["fec3f43d", "0740081762", "e04dd4d1@615c78e6.com", ""], ["90645f43", "0737009757", "d8ed6970@a5baa38b.com", ""], ["723c41aa", "0732060795", "d42e8f6c@9f01caf6.com", ""], ["96235675", "0755010192", "30d98b6f@fad581d8.com", ""], ["1bfda78b", "0736195102", "f0ba5711@24ec477e.com", ""], ["9fde0750", "0737641391", "f73cf50d@108aad37.com", ""], ["4cf28ec2", "0715717575", "f18063da@f0625607.com", ""], ["96824bd2", "0754345078", "f7b7534d@5c430ea4.com", ""], ["57e1a524", "0775075206", "9b963917@f4c93815.com", ""], ["1a1b212d", "0796156113", "0da3bd5f@60d5c0a3.com", ""], ["73267af7", "", "20364468@e070797b.com", ""], ["c4fcd602", "", "c996b53c@8c9e56e7.com", ""], ["23944725", "0756128345", "3f1043b9@4e705983.com", ""], ["8665f6ee", "", "c562cd48@d44eb942.com", ""], ["5178e72f", "0791852636", "bbcae5ee@428b8ba9.com", ""], ["9de66e8d", "0753385146", "55874b2c@f115714e.com", ""], ["de112089", "0734935576", "789b1706@6d2b21b8.com", ""], ["4e8283b9", "0700426636", "081d48fc@797f0558.com", ""], ["081d4271", "", "99829160@285f8fd8.com", ""], ["69dd706e", "0771351294", "906cc914@01cacc1a.com", ""], ["5de2de11", "", "1edc2268@e01bd776.com", ""], ["e823c600", "", "ecda6d9d@74a5da65.com", ""], ["907e7635", "", "14cbd73f@3c5029c4.com", ""], ["d6236d57", "", "39f3d4ae@1b7b8200.com", ""], ["aa2630bb", "", "75535cee@5391b394.com", ""], ["c6cda97f", "0710755065", "ff4ab3b0@e7638240.com", ""], ["b6661f85", "", "067496c8@1c3d2df6.com", ""], ["c95cf777", "", "afa9a3cb@279aa011.com", ""], ["e575e109", "0785713068", "8601eb2f@7d69d9ef.com", ""], ["c4aed4d1", "0797133251", "0ebd0ca0@44b0db13.com", ""], ["d8799ad6", "0766336482", "c8a2db59@fc20467a.com", ""], ["fe041483", "0747021458", "f1341509@a0d4bfcb.com", ""], ["9c8975f4", "", "1adf2f38@6ef39e01.com", ""], ["fc572eb6", "0707977477", "39ce98ea@c55bd59e.com", ""], ["86b7eeac", "0714499420", "19318930@f8cab44a.com", ""], ["d64977ad", "0761071885", "11a682aa@69ca2391.com", ""], ["ea006f73", "", "dfa103d4@ec0cffab.com", ""], ["0137c08a", "", "4e98808d@f9cbe2ee.com", ""], ["4c3ef173", "", "ad18e541@9c961399.com", ""], ["3a90243d", "0702089544", "73d9e758@a8185f82.com", ""], ["552fcf54", "0711153208", "b425511b@1f62462e.com", ""], ["47f20958", "0709287769", "387a2338@24efeaca.com", ""], ["95568c92", "0753062212", "230b16d8@7c47aa21.com", ""], ["4e00e50d", "0705656877", "e0557aa4@17b9f5a3.com", ""], ["6a744fbc", "0752791693", "f1cb0467@fc01b034.com", ""], ["b00241b5", "0707154247", "8fb98d05@46674fd5.com", ""], ["40bbff33", "0798115729", "238dcde2@3a402b74.com", ""], ["2ff0e839", "0734208035", "b85bc3b0@fb7c2ace.com", ""], ["48db2fd2", "0746707903", "21bd36a0@967fc046.com", ""], ["82ce2569", "0712225501", "b578f1ac@ebb9345c.com", ""], ["bc8eb82a", "", "579a208c@3a587c16.com", ""], ["e6c2ceff", "", "cf4f1954@a02d98a5.com", ""], ["36cc5274", "0794054613", "6a6fba68@59b878ab.com", ""], ["a47d46b0", "0779573055", "7e3b995d@8ba62af1.com", ""], ["abc2eea3", "0770887487", "2d0b55d1@9b58d90f.com", ""], ["3aeec47e", "0798128924", "cc01849a@23ae0c8c.com", ""], ["438153a3", "", "fbf57f49@b868a49c.com", ""], ["c2e79acb", "0790684868", "eefb75e0@e0e96a2f.com", ""], ["975b014e", "0762757057", "aa6a37dc@a30b0c72.com", ""], ["e610d666", "0781184369", "4d3f4b20@97d8a401.com", ""], ["73495ac9", "0719719687", "7a018884@c2c13e7c.com", ""], ["9a445855", "0785545967", "2d196afc@ffff6d02.com", ""], ["ce2ab926", "0700868262", "2cd6763f@b6d56901.com", ""], ["c0fcbafb", "0706968744", "8ab42ade@5692f6e3.com", ""], ["0604c52c", "0772596476", "b71e2f2d@31e533dd.com", ""], ["b4fedfd6", "0700359913", "af1c4ae4@fcbb6631.com", ""], ["1e19fa35", "0742671783", "009e471f@8e4f29d9.com", ""], ["6bc8631a", "", "38a6dc3d@900b535a.com", ""], ["09c221a5", "0712627371", "6b081654@a5d250ba.com", ""], ["55d80d5c", "0710423548", "149c66a4@0adbe042.com", ""], ["bc6f4369", "", "28372894@dfa1fbcc.com", ""], ["7aa8a6a4", "0766166352", "9dbb5873@5731fed6.com", ""], ["5c781ef0", "0790623819", "2d629650@b57c316a.com", ""], ["6830c71c", "", "39897b3b@7ee8cde5.com", ""], ["6af8ee98", "0747825215", "5b56cf20@4c81a082.com", ""], ["5a8bcde3", "0763197608", "e69532c0@34630f3b.com", ""], ["8f9037fe", "0726240186", "df9e5814@e4fc4423.com", ""], ["fb13d5b0", "0721173617", "d475522e@c02f6cd8.com", ""], ["9ce57e77", "0774209520", "288a94c3@2a0fba22.com", ""], ["b54fd99f", "0735610926", "061e00aa@34301c70.com", ""], ["fe1a320c", "0776730222", "53770fab@f7c2cd27.com", ""], ["10c3b647", "", "ae78b399@9c814e5f.com", ""], ["d24be5da", "0797581905", "9394a573@bc5e7a67.com", ""], ["e61273a7", "0730217168", "ef553bd3@fbd3732e.com", ""], ["fb6472b6", "", "cdf00170@f2e89868.com", ""], ["b588bcf8", "0715004480", "21505721@1ce9749f.com", ""], ["36ef226a", "0720093875", "293e85b5@c81c476f.com", ""], ["007c51ec", "0770116496", "b1b68fa0@159369c4.com", ""], ["949c6074", "", "6e66fd1b@434382f3.com", ""], ["4e4f11d2", "0762955197", "66d29d82@531b2e83.com", ""], ["758c58d1", "", "9dd36cae@295af2b4.com", ""], ["99be0c22", "0794398863", "0249fb4e@4786d822.com", ""], ["1298fb9d", "0720206151", "d3c59051@5d59357f.com", ""], ["b97f877d", "0779925184", "f6414a10@13ffe42b.com", ""], ["3e653aa1", "0750192219", "b35e6fe5@1b6bf57b.com", ""], ["8fbab13c", "0732153634", "b0a7711e@52ecc07a.com", ""], ["1a3e75c1", "0743946942", "25786349@8c65834c.com", ""], ["6ca639a0", "0717692914", "ad3be222@b10a9147.com", ""], ["3c8dd318", "", "bcccd36e@cc70d14f.com", ""], ["a6d3e7cd", "", "aa9c8a70@963098a4.com", ""], ["6fc1b3a2", "", "d19b5d6b@934e24f1.com", ""], ["759261f1", "0709242692", "72f51517@87b24cae.com", ""], ["3d7a1d1e", "0715207809", "877c9255@1671ff51.com", ""], ["93278583", "0742390782", "21dca350@2cb6d4ef.com", ""], ["51f96553", "0748353883", "6be176c3@4e96ba7a.com", ""], ["a347fde6", "0741522657", "c133fafa@26d07149.com", ""], ["362bd97e", "", "60ac173a@10183062.com", ""], ["b1a7c0ff", "0764817452", "67e54fec@06bf6f87.com", ""], ["9ee4aa89", "0741377765", "1856ef66@cc9f3fcf.com", ""], ["49a7dbd1", "0783985348", "ce3970b4@ea82bad4.com", ""], ["206b0643", "0767461177", "a897ff67@8adf5df6.com", ""], ["596a240b", "0722068325", "82663950@9a960206.com", ""], ["dc852f2d", "", "381e1687@8bc841b8.com", ""], ["44dbefa1", "0774987561", "391ecd0c@26e9e890.com", ""], ["69e6e40c", "0713319482", "978839d0@5ad63633.com", ""], ["0121d5a7", "0714766437", "8abfc0e0@b6e025bf.com", ""], ["cd5200b5", "0764514081", "6304987a@1d2f7eb9.com", ""], ["536c4831", "", "c0e01f58@cc7a1d56.com", ""], ["31167693", "0712547344", "9e54c7e9@90b5ff9a.com", ""], ["6a240068", "0776430272", "4ae0eabc@345cd527.com", ""], ["c14f594b", "", "7351d1ff@31c0438e.com", ""], ["b290c6f6", "", "9368d514@98711325.com", ""], ["f870b7b6", "0789240289", "4f8593ba@f80fe1b6.com", ""], ["f9c1a49b", "0714414302", "02efd04f@a4f155cd.com", ""], ["51605326", "0704713807", "13a21489@ec8993c8.com", ""], ["88b5f2e2", "0794091627", "a2745c7e@a32c3b1d.com", ""], ["f3b6a422", "", "4e024c4c@022f5e1f.com", ""], ["c2cfc88a", "0761247770", "153b9fe1@9859b8a4.com", ""], ["597f3524", "0761882451", "1982d242@a8f03583.com", ""], ["d694bc4c", "", "864732d7@76277920.com", ""], ["8e859b4a", "", "d16f7f69@0261a0ef.com", ""], ["2d583afa", "", "876b76c6@e55415c1.com", ""], ["1bd3b1ae", "0761041162", "a2ebbee4@9a1b4f54.com", ""], ["a01d3c8b", "0703532365", "d6d7b818@bf578a2a.com", ""], ["6b8ff331", "0705773074", "e1e26a20@144139a8.com", ""], ["bef24803", "0750088813", "fde0039c@4ed43033.com", ""], ["253651f9", "0715013226", "3aebf85d@f7455c19.com", ""], ["34da6189", "0764808908", "03f2bd4a@c73ec94e.com", ""], ["c72f3422", "0754811376", "5941e3ce@769439b1.com", ""], ["1761f923", "0708249762", "20e78457@68b640c2.com", ""], ["a7ddd99c", "0754994431", "ad342d73@622fa530.com", ""], ["1b32b5cb", "0769400739", "510cf15d@1feaf7a5.com", ""], ["2ecd5127", "0788462377", "f2a96f71@c6dadef6.com", ""], ["b2e338ca", "0780233422", "0394850d@c7f808dd.com", ""], ["0999515c", "0708264022", "9214c832@73793633.com", ""], ["b4662820", "0791822790", "378843d9@faad02b6.com", ""], ["b6526e56", "0702391559", "ddd3922e@683a9bc3.com", ""], ["e1d0dfca", "0796434690", "bbea0324@a25f259b.com", ""], ["7507ff74", "0718970953", "957feb53@dd898780.com", ""], ["537fba55", "0712047297", "380f1934@1afa6994.com", ""], ["c03244c8", "0719594534", "d685945b@5f863d81.com", ""], ["7a0b4463", "0726964918", "7dada8d2@632a578b.com", ""], ["01f105bd", "0771635491", "0103cc3d@2c004125.com", ""], ["7c9baaea", "0793949807", "b0aae8e3@23796a56.com", ""], ["05f636c7", "0707511947", "36c7c75f@2d86967f.com", ""], ["71d917a2", "0784386161", "ad18b2df@9366e77f.com", ""], ["d417a204", "", "e3ef2306@48c768ce.com", ""], ["8d6d1bb3", "0705778200", "ee43e84c@f6df11fa.com", ""], ["f4c05035", "0772727935", "c0ced236@b4ea187c.com", ""], ["e6d75e18", "0788952581", "402b9e4d@0b37e829.com", ""], ["1dabeec9", "0727510448", "a0fac12e@0ea007ab.com", ""], ["f71fa168", "0759368909", "2b111301@22da70d4.com", ""], ["23b7c929", "0786848255", "b71bbd84@857f9ec8.com", ""], ["a085c14c", "0740213711", "d32148b7@b67a73ae.com", ""], ["9ac9fa3e", "0701707031", "42cbdd1a@175f318e.com", ""], ["b4e40dc9", "0715864237", "a15c733d@6669b4f7.com", ""], ["94020cfb", "0715193084", "ad45863a@2eaab246.com", ""], ["626c1ee7", "0750957414", "d66ae397@2e895e2f.com", ""], ["1eabc823", "0731567529", "20f8332e@b4b0703c.com", ""], ["e5a98097", "0718031536", "718b7ae2@8504fe2d.com", ""], ["69245c65", "0779488230", "fc645fef@635fbdcf.com", ""], ["247ce107", "0721718937", "2c662519@b0c33ecc.com", ""], ["c4925e4c", "0710565488", "36e178bb@4cb2efd9.com", ""], ["d0ef8f45", "0725811338", "f5404bce@9e12cd0a.com", ""], ["ed80a2b0", "0708451045", "6b9f4393@5ea5c209.com", ""], ["13aab094", "0711289294", "ce0972c1@c202849a.com", ""], ["0154dcda", "0734530650", "e225ca87@12039c6d.com", ""], ["004cecdb", "0741339169", "d85299ff@ce7f5fd2.com", ""], ["bbbcbeb3", "0723560169", "e31d0863@b105423d.com", ""], ["cef7c1a9", "0752786167", "3c76459a@4e492e4a.com", ""], ["c3cb922f", "0779170714", "24e2fd0a@cd086652.com", ""], ["a267897d", "0740299573", "c8ab2622@5d1d5225.com", ""], ["f73c9d67", "0754733442", "433436bc@eaa41a7e.com", ""], ["bb0022c6", "", "016e3dfc@ea644733.com", ""], ["060c6c8f", "0767126800", "24a3b50a@8c364ded.com", ""], ["9f5b5d9a", "0769176462", "20dc7747@ef1ad5b9.com", ""], ["43e67ec3", "0743483082", "8a5f94e8@5e7d39a0.com", ""], ["345e4a33", "0787677047", "3a0c802c@608dbe31.com", ""], ["ec2e7f7d", "0784990090", "23ec5d05@c4bebf51.com", ""], ["e239c87d", "0738679004", "8cfa7063@bcb2bc21.com", ""], ["271fc9e7", "0778470494", "c2d46c92@dcb3dbfc.com", ""], ["3b0585bd", "0795454441", "40772365@1d8f7187.com", ""], ["a1a58875", "0718370412", "2eb10d10@5ec547bd.com", ""], ["4c45c47c", "0739708464", "81d5c8ac@06447b51.com", ""], ["e57e8359", "0751620730", "4b2b8980@c95b69b5.com", ""], ["735270e1", "", "ee37dbee@25a4ff65.com", ""], ["4b44e00f", "0711946373", "0eb6a22a@b320af8f.com", ""], ["8a372c1e", "0770968660", "d6678940@ee818cf4.com", ""], ["88efebf9", "0720205646", "7d6d6bcd@87fecbbb.com", ""], ["6ebf214d", "0780223716", "d28759e2@e323f23f.com", ""], ["2eb46280", "0795896930", "8dae7897@d3854b08.com", ""], ["53bc97c3", "0730645626", "12518a5f@a1fdfaca.com", ""], ["fec1e211", "0754874523", "40655f66@4897f637.com", ""], ["1b8ef046", "0731698966", "cbed8d9a@893a95be.com", ""], ["70099e91", "0725267567", "b75efdd0@8dcf7f96.com", ""], ["00234cba", "0748766899", "9088d159@a1a1f4c1.com", ""], ["35ff23c6", "0747870199", "c961250c@d8a06d24.com", ""], ["9e7417f8", "0746801322", "ba7e50e4@cae69ff0.com", ""], ["3428f8d3", "", "56c21910@48bf2959.com", ""], ["f575e8a6", "0734183553", "4953d608@e2885866.com", ""], ["ee701421", "0762346436", "0eee7492@884b62a5.com", ""], ["197c760a", "0741186587", "8e5f6e2b@ea3cb8da.com", ""], ["691a46aa", "0708195042", "cf89f958@dfa6105e.com", ""], ["b03db786", "0716744355", "3f1c117f@a6c83666.com", ""], ["4be4d96b", "", "48195284@40467121.com", ""], ["dfcd1ada", "0783073475", "9785f27d@1df650f2.com", ""], ["4490c17c", "0786052205", "8e4bab0c@b98b8c4c.com", ""], ["f4e991f7", "0717102345", "e5a0a4fa@0483a00c.com", ""], ["b15003cd", "0771528694", "e012bb67@b1463416.com", ""], ["95e2a50d", "0796484412", "508dbc66@25ef9b72.com", ""], ["c2003313", "", "8856d03c@72d7544b.com", ""], ["276b4bc0", "0789810903", "ef87caba@2741b9cb.com", ""], ["15da925e", "0748450892", "656949ba@78234623.com", ""], ["77e171e3", "0786551610", "a86b4d19@110baa19.com", ""], ["9d9d0504", "0758342738", "0dc0e069@bb5e721e.com", ""], ["f4def247", "0798052097", "e66df9a7@af41f423.com", ""], ["c3ea412f", "0706797533", "4e28af08@8a754fb6.com", ""], ["53812400", "", "6c6215f1@45e33532.com", ""], ["369bfda8", "", "45cf4bdb@6d0980f6.com", ""], ["dbd3b821", "0765939770", "f448784d@35d292f5.com", ""], ["142f350f", "", "41c305cd@fdf34481.com", ""], ["8fcd1626", "0770444423", "323d955b@41ff7d99.com", ""], ["8789a603", "0751524803", "359bc02a@72af402c.com", ""], ["0a675687", "0789901250", "2323249a@619f78c5.com", ""], ["fb011bf6", "0750579472", "7152dcf1@668f96db.com", ""], ["a623d8fd", "0733289400", "482d64cd@8a4e2926.com", ""], ["6d961e75", "", "6fe4f5e1@e9239485.com", ""], ["4f39910e", "0746456899", "e85969c9@cca744af.com", ""], ["81a834a8", "0731458518", "8ffce3c8@8e7ae609.com", ""], ["70472837", "0775526068", "5dbfd52d@0d829cb6.com", ""], ["2d882041", "0708561610", "e5853dfb@b2187974.com", ""], ["deaf54bc", "", "a9dc3872@2506f15e.com", ""], ["a7226533", "0760964433", "2490dcc2@de364340.com", ""], ["36377c3b", "0759911971", "93465967@b06cdaa6.com", ""], ["31724c44", "0757697274", "b04d8ef1@544a5467.com", ""], ["51806270", "0742044114", "417f3dca@cab99b90.com", ""], ["e9b2295d", "", "d9e360f5@4d87b309.com", ""], ["0dee89bb", "0712052963", "4700d9dc@8b9161ce.com", ""], ["8a4b9e30", "0755847836", "b3d261a8@60e7e6d9.com", ""], ["f8f7bc44", "", "ffd58a0e@e40c3e44.com", ""], ["c5d5d713", "0790256137", "35f89e39@5893f2c1.com", ""], ["90f0cbd9", "0737155772", "6d8e7f05@40038b63.com", ""], ["eef1d1a9", "0742179171", "065c13a8@634d0446.com", ""], ["55faa2a4", "0748611843", "22382d4c@d11eb075.com", ""], ["f7913ace", "", "40be2255@400d8d4e.com", ""], ["0d47fd15", "0797162254", "06ec4142@00a16805.com", ""], ["906a5fb1", "0703201953", "4d249ff5@55ffe749.com", ""], ["4b5010c3", "0741317092", "25d81157@6027b9e1.com", ""], ["f79b697f", "0773396997", "2e116cf9@9a6bfcba.com", ""], ["c6d2d0f0", "", "a83108a9@40159f26.com", ""], ["da24f341", "0775328597", "4e7592d1@70f4d9c1.com", ""], ["b20edca2", "0789450137", "d326baf5@1baafa6e.com", ""], ["3e11a76a", "0776368949", "15a351bc@e4b229ff.com", ""], ["3e2fbdc3", "0717744150", "4ebbd18a@de822931.com", ""], ["7e6c9549", "0750887467", "2c5034ed@23402dc1.com", ""], ["9536e16d", "0713928189", "e6cd0a97@d20db0c5.com", ""], ["8358cf78", "0735864901", "4ea98e6a@e1052078.com", ""], ["d12cef0a", "0730971666", "a7f6061d@b6f2822f.com", ""], ["cc2ee54b", "0778562618", "61917c3e@032bcdb5.com", ""], ["21ef6091", "0763511033", "166d075e@522d8051.com", ""], ["f6b7a396", "0772929913", "13ae6bf9@c44fbe8b.com", ""], ["042555cf", "", "b11161b5@143fc047.com", ""], ["9739c45e", "0792544143", "9a57d50c@7545618b.com", ""], ["aad5cccf", "0781328164", "59e7123e@488f0065.com", ""], ["b886493a", "0785224849", "d6499e6f@0688b326.com", ""], ["2c99ee07", "0748501224", "1e582961@799a7165.com", ""], ["72545b19", "0715248877", "637c5be0@c9d62b4b.com", ""], ["6f038b04", "0717198089", "fa325ae7@624b634c.com", ""], ["4f8e0b2a", "0764277265", "cb4a5eab@35f7346a.com", ""], ["03fdeddd", "0721789994", "c0476d68@4d5cd7bd.com", ""], ["89d74e2d", "0760756190", "d6594d52@5dee56e4.com", ""], ["c24acb5f", "0781827089", "3a1ea265@8bd8e1cc.com", ""], ["d1bf9230", "0722180686", "9e582b1e@e8351d46.com", ""], ["9dd2e88d", "0711860229", "56658ca4@132ac241.com", ""], ["a07824cb", "0741457266", "76f4cfea@f25c3adc.com", ""], ["f3897859", "0710503175", "8f23c250@4540617e.com", ""], ["c09a14dd", "", "e9c464ac@f76cfe06.com", ""], ["c9a758e4", "0790149404", "b5fbb29e@2ef70cf1.com", ""], ["dd56f5d4", "0709704673", "8b24db91@663c1804.com", ""], ["1ccf7b2e", "0728115684", "d7610e72@e4a8b643.com", ""], ["32351dff", "0748120068", "3939d0ab@8fdf1e7d.com", ""], ["8c4053c5", "0714496140", "420781c7@8ccbf30e.com", ""], ["ce266357", "0784144894", "de2e719c@22b747ed.com", ""], ["3257e91e", "0783497860", "09f03ee1@a98f49a0.com", ""], ["0180d6f1", "0743825253", "ec4bf604@a0b51abc.com", ""], ["5360eee3", "0719368035", "bb270d1d@984f9d39.com", ""], ["d40e19a5", "", "5ccc6f37@d6870955.com", ""], ["a429a2e0", "", "9372812a@b1676aa3.com", ""], ["45dfc0c2", "0709417666", "fed1b00e@bf665131.com", ""], ["a173558c", "0758991067", "3f706fd9@301776a7.com", ""], ["ca87b3af", "0781867475", "98155b79@adcd5507.com", ""], ["036da0ce", "0778243834", "66467ae3@686faa6d.com", ""], ["c5ddfc00", "0750665015", "4e0a5924@9d77bef0.com", ""], ["40b39118", "0765997451", "7bdfe708@a3cee04d.com", ""], ["a5de4669", "0761205980", "bfac2446@e1515b33.com", ""], ["a62448b2", "0739220943", "3da472cc@73d9e74b.com", ""], ["3b04bf59", "0711824354", "b6ac5778@42772967.com", ""], ["ab56dd06", "0791996063", "3949b2d8@b08c4c5f.com", ""], ["9820b796", "0724164512", "ecad52ea@81041046.com", ""], ["894e5af9", "0706784563", "4b2ee10d@821e0a2e.com", ""], ["dd06ec54", "0782812141", "a2e91578@875cac30.com", ""], ["802f32a7", "0749216707", "8e7171df@898e4f66.com", ""], ["ff30e79e", "", "e09f32d2@e8bed799.com", ""], ["7070b09d", "0707078844", "bfb66733@308be7e8.com", ""], ["fde6085d", "0712529236", "c4750b1e@05266810.com", ""], ["87f4aafd", "0739190648", "1cb92876@1cf2f6df.com", ""], ["94a88efa", "0775446056", "f0ce2f87@410508db.com", ""], ["20f56909", "0740230843", "55997ce7@82f437b1.com", ""], ["1ee26c6c", "0713640395", "6357a25b@fb48f794.com", ""], ["ee1ba135", "0789826448", "097f6423@892f4d97.com", ""], ["7fa6baf5", "0752582669", "6e49ac6d@7012d5a1.com", ""], ["35fc8c1d", "0789186620", "0abe0f3f@2435673a.com", ""], ["0e7434e8", "0791384205", "96c88dc9@8e26852c.com", ""], ["6a899564", "", "65902813@1686d624.com", ""], ["90d1935e", "0756326458", "8ddc4a04@a5572b9d.com", ""], ["f8c69667", "", "c827107a@93f105af.com", ""], ["b2c511ea", "0749010920", "24bb2d69@f4287f85.com", ""], ["6ecc592e", "0788769694", "4c132913@931ce84d.com", ""], ["6722b9db", "0794262991", "52bed401@35003685.com", ""], ["6874d990", "0748452679", "b5dca8b6@97c1b768.com", ""], ["72740c63", "0721748699", "9e10c395@a98c8e1c.com", ""], ["ee0d78e0", "0795469696", "56d8a295@d6ef7c7e.com", ""], ["d5bcb2aa", "0741284724", "34c858aa@5f49689d.com", ""], ["5909a519", "0744679678", "88409af7@e19f4243.com", ""], ["f265acfa", "0792029151", "e34d1172@0e9bba98.com", ""], ["94a5602e", "0709917575", "0464515e@552c3013.com", ""], ["d0d483ad", "0764326930", "86b8c70f@a6382da0.com", ""], ["b9155373", "0725007352", "e3223601@f5a902c8.com", ""], ["600e630d", "", "f4e4d66b@bf8aa87c.com", ""], ["fe12bd24", "0772663541", "8b8b1297@1e02a9bf.com", ""], ["fc11b86a", "", "65f3c1ac@375002e4.com", ""], ["110d8fa4", "0775226701", "3d955f6b@ce7da885.com", ""], ["d3105be1", "0779779352", "ed2be0eb@ccc7485e.com", ""], ["fda61a0b", "0720274167", "6becb3e3@4deb6681.com", ""], ["17f8be17", "0799234608", "73f14970@cb957e59.com", ""], ["e9516a1d", "0760461815", "8ad30006@8df0fd1a.com", ""], ["60177106", "0760558146", "dc029d5c@b6ef2fd2.com", ""], ["3450866c", "0711798090", "cda58109@c2e25542.com", ""], ["06e26393", "0735961481", "305bcf19@6c3ec630.com", ""], ["708af001", "0728438353", "048b067d@80d72f50.com", ""], ["c40ec0a3", "0726279359", "a10d333e@9a89cb7c.com", ""], ["2a2eb847", "0741461630", "d3daf952@7a3408e4.com", ""], ["d01a1813", "0704645200", "f226e03a@f67c7d10.com", ""], ["819cb8a9", "0798337915", "cf70d59b@d1a42e77.com", ""], ["6fa32598", "0701415106", "4a225f04@2534e6a3.com", ""], ["4e441149", "", "456f82ab@ca0b04a5.com", ""], ["f7ff1a4a", "0701515624", "5918544a@4bb3704b.com", ""], ["5738b682", "0724843216", "04c74b30@ed510bac.com", ""], ["f10e2d6a", "0730727806", "415d4081@b1a88a68.com", ""], ["749e83bd", "", "15e0671b@76428121.com", ""], ["3d65b85b", "", "d1eb4053@c363e69a.com", ""], ["a0792a15", "0729947768", "c24f344e@41f87b82.com", ""], ["fb01d572", "0792621720", "9c750d18@7fb1f5ce.com", ""], ["e67acc97", "0789099675", "bdfbe8ff@e2ca97d6.com", ""], ["a9e6cd4b", "0708488791", "1826128f@98d76c9c.com", ""], ["819f95a3", "0788991943", "cf458e5d@44f3c946.com", ""], ["20204d4c", "0714720322", "1efe1d68@5dddd6aa.com", ""], ["a1ea35bb", "0749468567", "480f2ab1@72a338ed.com", ""], ["92ffc165", "0780038157", "4dd11686@daa7a1e3.com", ""], ["4ba3f501", "", "71b12e98@a76b654f.com", ""], ["d7f5285b", "0736359794", "fc0e04a5@dde7291b.com", ""], ["77bfe67f", "0742014858", "b3c8790c@cacf5548.com", ""], ["dbf35774", "0726444745", "2243d1d2@138dfd54.com", ""], ["e9f91a9f", "0760618445", "db7cbba8@4616651c.com", ""], ["04c6be09", "", "9adf3814@5e01afe2.com", ""], ["b57e7ee3", "0722578719", "1736e501@964d3424.com", ""], ["d8114397", "", "6bc5e72a@bc4d7e37.com", ""], ["7ab21681", "", "aa33cd18@d6af8a2b.com", ""], ["290e81e9", "", "4d6f5933@bb9b4aa8.com", ""], ["5a1f2ac9", "0778832081", "53b60c08@8973dcfb.com", ""], ["f8775a07", "0743005567", "3c53f38a@c0918592.com", ""], ["30c7cdb1", "0779422190", "a809f36f@1b3dc9e3.com", ""], ["1c669546", "0793826953", "0b2728a3@f2f4eee2.com", ""], ["229beef7", "", "97621f15@237e6070.com", ""], ["ab8c48d5", "", "8545951c@c7523008.com", ""], ["e4294ab1", "", "6d8bdfd5@e9c1c910.com", ""], ["c9c8fbe2", "0729515527", "bcd2115d@e4500586.com", ""], ["0b6463d3", "0725941332", "e768eb3b@fbd48ad0.com", ""], ["15bbab63", "", "a8fd2422@b236a910.com", ""], ["c181cff0", "0717305018", "fb4e37ed@b611bc36.com", ""], ["03f712cd", "", "a05576b0@e16b8368.com", ""], ["31105e59", "0790432797", "6e4a7e2a@d6902424.com", ""], ["5c868ce0", "0738424344", "36348d0f@342e6bed.com", ""], ["d47bd754", "0793838363", "dd9d97d7@54a56407.com", ""], ["660908ec", "0707537040", "ae9b6681@a9382430.com", ""], ["2c6290a0", "0759084568", "b011def2@e1ac3f5a.com", ""], ["a989f05a", "", "25e59710@79b7f417.com", ""], ["f3c16d3b", "", "ded9819d@a99d7dc3.com", ""], ["1adf7818", "0751467205", "b96aa90a@9791c269.com", ""], ["409cc67e", "0777500182", "5317c827@a2db4c45.com", ""], ["aabd103f", "", "e5d907aa@bcc71e10.com", ""], ["d23bf430", "0777169222", "85e8641e@cc5f3d8b.com", ""], ["69a2879f", "0743889293", "4b5f4141@04205775.com", ""], ["8a3ec274", "0756861440", "ee663794@82c1f4fe.com", ""], ["b4a3cd48", "", "fc399765@64ca060b.com", ""], ["e0ccaed3", "", "6388033c@824e8b90.com", ""], ["c3eee2b9", "", "c0162073@2c2fd2b2.com", ""], ["a73c64c4", "", "f688a2e5@6e30672c.com", ""], ["2d852e73", "0772174434", "7a42d0d9@72486cd3.com", ""], ["a12254f5", "0769384878", "caf08e44@d46ed98b.com", ""], ["f653b74f", "0763997467", "388c7213@1126f95d.com", ""], ["fc93ee15", "0774848627", "beabdaca@2d84271e.com", ""], ["73c04bfd", "0771623831", "8fe53552@31ce4032.com", ""], ["74a5f23f", "0789722672", "82930bac@7dc3e501.com", ""], ["796d3e3d", "0762835594", "1181180a@ec66e2f0.com", ""], ["1bc14fc5", "0704902009", "ed326523@23e42c30.com", ""], ["5c08dcb0", "0765115244", "dd5945cf@f297b86b.com", ""], ["566bf582", "0723354400", "ea731083@ef83ec7c.com", ""], ["f25208d4", "0789906022", "457e0601@4eb796dd.com", ""], ["114eeeb3", "0758751442", "14cfa76b@aee76d12.com", ""], ["2669d4a2", "", "5ea1e2ac@cf638cf0.com", ""], ["6202f1c1", "", "d93e09a6@d3898452.com", ""], ["5126ade5", "", "96601158@cfeca444.com", ""], ["a0cef71f", "", "e9366869@532d79f8.com", ""], ["41bda0e9", "", "c4206b97@966c822f.com", ""], ["a75dc6cc", "", "12906204@892175ee.com", ""], ["79636bcc", "", "c45c9aae@a303f7d9.com", ""], ["e40fb55e", "", "72fe75ec@c1ddfd45.com", ""], ["e0ddb510", "", "ea6c8b14@193b1240.com", ""], ["c7532313", "", "a9361a51@f562d91e.com", ""], ["0158d9f3", "", "1de7b7e6@876beb92.com", ""], ["b2394690", "", "fe1b9570@3cffd682.com", ""], ["79051273", "", "fbb85c72@4532b2d6.com", ""], ["1ea2aeae", "", "6e8d529a@fa5af449.com", ""], ["2c637d3a", "", "b633e7ca@b2e822e5.com", ""], ["ba3b59cc", "", "10e596a4@e2654efc.com", ""], ["631c774b", "", "03b0156e@5f3383bd.com", ""], ["6e4d8e1d", "", "b7026e82@015d71cc.com", ""], ["3d430d86", "", "6af17483@bf92c6e9.com", ""], ["ea26cfde", "", "aeee3cf2@e06f3387.com", ""], ["16596f6f", "", "d1445706@a3282036.com", ""], ["9bc58d49", "", "", ""], ["cc907372", "", "a923bc16@c3febc01.com", ""], ["605dd0be", "", "49daa341@8ee4c07e.com", ""], ["1b1114f0", "", "ec5a8a0b@489a7dd5.com", ""], ["28b0c962", "", "", ""], ["63370e89", "", "44d07879@3c414118.com", ""], ["f3f1db57", "", "", ""], ["ac49009b", "", "e5cd7740@ea261a3b.com", ""], ["9e46dcc0", "", "83a47ee1@a58545dc.com", ""], ["2e95367c", "", "8bee6be7@8906c53f.com", ""], ["ea22c7bb", "", "53b4805b@af24dcf7.com", ""], ["c90f526b", "", "d790585d@8d10cf2a.com", ""], ["7887a62b", "", "7120351b@ff19865b.com", ""], ["64cb3f9d", "", "", ""], ["9e591fa7", "", "c7730953@a714f3a7.com", ""], ["1f16cd07", "", "67bebc78@40ed8809.com", ""], ["287ec13a", "", "9e5e71cb@2bb1c891.com", ""], ["d164c461", "", "1cd6e067@14c6ada1.com", ""], ["36379fd3", "", "72a5809d@9b00b1bb.com", ""], ["9470c307", "", "2816f5ec@451ff875.com", ""], ["1655c648", "", "f06ec4a5@f5ab6d4c.com", ""], ["879040ac", "", "62e235cb@ec8a0c43.com", ""], ["50232cdf", "", "025cd190@2477e789.com", ""], ["5a35f64f", "", "", ""], ["777de661", "", "38640b10@41514086.com", ""], ["705fb8cf", "", "7284860f@b222b1f8.com", ""], ["fcb30c1b", "", "361fde21@1f411552.com", ""], ["079e7250", "", "6bbcefcd@2aeb37af.com", ""], ["4e47023c", "", "d23b6b4d@b0b56474.com", ""], ["1bce884e", "", "1dfab7b8@68a3a0ef.com", ""], ["7088e56a", "", "6570590b@e2a30ae0.com", ""], ["65ce20d2", "", "86106467@58070f61.com", ""], ["68d599aa", "", "e279dc2a@58d99123.com", ""], ["0142c624", "", "9abe7329@cb4a4022.com", ""], ["9e8bcae3", "", "840f16ca@1ed99fc8.com", ""], ["4b854254", "", "fbf12ee9@6499201a.com", ""], ["615d5c95", "", "abce7c9f@ade377af.com", ""], ["d389a4b2", "", "a78f0fc2@a6f959cd.com", ""], ["38be0158", "", "bfa96d44@f363eb7c.com", ""], ["f9e06f7c", "", "542bd29b@97746a9e.com", ""], ["832811a7", "", "9bf9010e@01957fac.com", ""], ["359a78e3", "", "3248a6d3@763ddb24.com", ""], ["27f9817c", "", "07d94000@2aca2301.com", ""], ["013a3bd4", "", "2224822e@fade404f.com", ""], ["a0ef9d39", "", "", ""], ["b024d6ee", "", "0daa6bb1@2ef82615.com", ""], ["a381acc3", "", "bad81f63@dc7acb79.com", ""], ["7610e7b9", "", "59829816@d8a49cb9.com", ""], ["4460acd9", "", "acf4b236@dd8a5e68.com", ""], ["5193bc70", "", "f5620364@f763b237.com", ""], ["9d134fef", "", "df0234b6@ae3b90cf.com", ""], ["8d8c4586", "", "f92d2add@0f93928c.com", ""], ["2d12e299", "", "99162fbe@59bd4569.com", ""], ["bcc4641b", "", "afff34a5@f2e5d247.com", ""], ["5c2243c3", "", "3ef0d599@f34a9ed2.com", ""], ["427913d9", "", "086fe7e8@925e9a12.com", ""], ["3a60c0ec", "", "66c4dfbc@cee34594.com", ""], ["ffca31e8", "", "0a4a245c@68e91e5e.com", ""], ["7df057ab", "", "335cabf9@a17a61b3.com", ""], ["71307e65", "", "c1de44cd@a1add235.com", ""], ["17bf2c39", "", "601cc244@f0efd775.com", ""], ["a8b9e2e1", "", "de175716@d0cfb7a0.com", ""], ["a294ae4d", "", "", ""], ["37859daf", "", "376f0f2b@369a7bcf.com", ""], ["01e1685f", "", "6fed663d@3672e214.com", ""], ["0a32e0c6", "", "0b0791c3@2c33f23e.com", ""], ["5b8ff540", "", "29599ad0@548964c2.com", ""], ["eec1094b", "", "dfa57b8f@80b6e55e.com", ""], ["acf49b4d", "", "4c20a1c0@c6d0e041.com", ""], ["63867c55", "", "", ""], ["873984c8", "", "1cf228b4@b15a1b5f.com", ""], ["ecde582c", "", "cc419b7b@60107818.com", ""], ["039d5e8f", "", "5da65ece@1bb69031.com", ""], ["21f04ac9", "", "8ee84896@25275182.com", ""], ["735349c7", "", "117051ac@3a4fd4d7.com", ""], ["273761c9", "", "0184bda8@198fe950.com", ""], ["200dff3c", "", "cb311031@7c6cc49c.com", ""], ["80a68462", "", "5f3c6275@44263f25.com", ""], ["53ace4f9", "", "733a3c0e@c7fd4969.com", ""], ["484e9625", "", "bfe97c09@d2ef03d6.com", ""], ["b798a822", "", "cd531d09@aa763932.com", ""], ["04cecc11", "", "e173c817@3977a28a.com", ""], ["2a910e01", "", "5e200dad@5cf70a26.com", ""], ["540b1452", "", "c9462faa@cb7652ed.com", ""], ["80261f65", "", "fea8b5bc@e5018e45.com", ""], ["9e4fa7c2", "", "95f3f9c9@f265aefc.com", ""], ["f0227223", "", "702f9660@2006f10b.com", ""], ["500bb986", "", "40a22a5c@ed4055c3.com", ""], ["34c14a29", "", "3d2633f6@206a53a0.com", ""], ["66a868a5", "", "7a2b1e68@9cf601fb.com", ""], ["af8d6986", "", "", ""], ["1fbefa32", "", "e948b9f8@c0fe216c.com", ""], ["ee799cb5", "", "01013bb3@229547a9.com", ""], ["b2ce8d2e", "", "fe3750e1@ee50e669.com", ""], ["ae7c0d5b", "0727494158", "68aad918@674f032f.com", ""], ["1c55c64e", "", "efc9a2bb@1fec90ba.com", ""], ["2eef74a3", "", "238d16ec@14658003.com", ""], ["5ba48a58", "", "c6d52520@65f96971.com", ""], ["e650ca0a", "", "76be4eba@a74dba94.com", ""], ["f977adbb", "0789591713", "68b0f662@c01e961d.com", ""], ["ac083f57", "0718005506", "6443f242@7ed34530.com", ""], ["0c7080de", "0791608592", "4444abf7@63b54852.com", ""], ["599039e0", "0717263596", "34898b46@b248d961.com", ""], ["33656705", "0788380435", "0d6c86a9@0d0b30a4.com", ""], ["f0d6f309", "0766220442", "22d01833@9bba21ab.com", ""], ["7c27f7a0", "0740179768", "69d0ce4f@829116df.com", ""], ["cba407c8", "0745165304", "4e94762c@6033decd.com", ""], ["3a9c0bcd", "0738794804", "ea1ca3ef@bd6258aa.com", ""], ["006b2bde", "0759645406", "822277ef@e36775c4.com", ""], ["fe832f26", "", "", ""], ["03c43ae1", "0776208246", "8e64a53e@d0aefbfb.com", ""], ["4ae6a913", "0798677379", "4affe084@f4ee6d7c.com", ""], ["5d3d3ca1", "0755000779", "0ea61d50@7ffd96e5.com", ""], ["dc58f75f", "0717795899", "119adc66@edbc9bac.com", ""], ["593f7e23", "", "d3161d49@fe2ad83f.com", ""], ["e5075ec4", "0760361665", "2f410a97@1b02d6a2.com", ""], ["a0d86ad4", "0765965949", "0fb55b69@cd1c2b77.com", ""], ["09e6e8a1", "0790479077", "cc542c67@9cb69e36.com", ""], ["77bb6d11", "0720961023", "60ff31ae@d9349e56.com", ""], ["e2f4aed5", "0735608325", "663b0400@671dff5d.com", ""], ["149211a9", "0706545912", "8481da49@233dfe8e.com", ""], ["59a53ba0", "", "3401405f@e1c370ad.com", ""], ["60a4c106", "0746099226", "7003f4bf@6a7770b2.com", ""], ["4483788d", "0728712830", "b30ee032@1694bb85.com", ""], ["ee8a3129", "0712964023", "34ee5c80@c82fb116.com", ""], ["c8bdde49", "", "3ac6c6dd@13bc3322.com", ""], ["65a477be", "0705359300", "a67e1d49@966dedb0.com", ""], ["d1a51add", "", "", ""], ["467392b8", "0765663197", "f878eeb4@7334e9fd.com", ""], ["61b4a1d6", "", "9d7e523e@e334c7d2.com", ""], ["16bbecf4", "0794827711", "39dff936@596fb073.com", ""], ["1cd25515", "0786915799", "d1c9de87@fb823959.com", ""], ["9d653b08", "0783707353", "75f1c3fa@dca1ac44.com", ""], ["cb93adc9", "0757349238", "7c3f098b@cf9bb4fe.com", ""], ["5f879d9e", "0764695115", "0a710345@b99b17e3.com", ""], ["8b840055", "", "", ""], ["d436dacc", "0796095598", "ea120899@3c8f6ea0.com", ""], ["1d0faf93", "0744424802", "3cf588a8@44544be8.com", ""], ["4ee8b8f7", "0764292501", "", ""]]
\ No newline at end of file
diff --git a/openerp/tests/addons/test_impex/tests/test_export.py b/openerp/tests/addons/test_impex/tests/test_export.py
deleted file mode 100644 (file)
index 26219b3..0000000
+++ /dev/null
@@ -1,589 +0,0 @@
-# -*- coding: utf-8 -*-
-import itertools
-import openerp.modules.registry
-import openerp
-
-from openerp.tests import common
-
-
-class CreatorCase(common.TransactionCase):
-    model_name = False
-
-    def __init__(self, *args, **kwargs):
-        super(CreatorCase, self).__init__(*args, **kwargs)
-        self.model = None
-
-    def setUp(self):
-        super(CreatorCase, self).setUp()
-        self.model = self.registry(self.model_name)
-    def make(self, value):
-        id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': value})
-        return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0]
-    def export(self, value, fields=('value',), context=None):
-        record = self.make(value)
-        return self.model._BaseModel__export_row(
-            self.cr, openerp.SUPERUSER_ID, record,
-            [f.split('/') for f in fields],
-            context=context)
-
-class test_boolean_field(CreatorCase):
-    model_name = 'export.boolean'
-
-    def test_true(self):
-        self.assertEqual(
-            self.export(True),
-            [[u'True']])
-    def test_false(self):
-        """ ``False`` value to boolean fields is unique in being exported as a
-        (unicode) string, not a boolean
-        """
-        self.assertEqual(
-            self.export(False),
-            [[u'False']])
-
-class test_integer_field(CreatorCase):
-    model_name = 'export.integer'
-
-    def test_empty(self):
-        self.assertEqual(self.model.search(self.cr, openerp.SUPERUSER_ID, []), [],
-                         "Test model should have no records")
-    def test_0(self):
-        self.assertEqual(
-            self.export(0),
-            [[False]])
-
-    def test_basic_value(self):
-        self.assertEqual(
-            self.export(42),
-            [[u'42']])
-
-    def test_negative(self):
-        self.assertEqual(
-            self.export(-32),
-            [[u'-32']])
-
-    def test_huge(self):
-        self.assertEqual(
-            self.export(2**31-1),
-            [[unicode(2**31-1)]])
-
-class test_float_field(CreatorCase):
-    model_name = 'export.float'
-
-    def test_0(self):
-        self.assertEqual(
-            self.export(0.0),
-            [[False]])
-
-    def test_epsilon(self):
-        self.assertEqual(
-            self.export(0.000000000027),
-            [[u'2.7e-11']])
-
-    def test_negative(self):
-        self.assertEqual(
-            self.export(-2.42),
-            [[u'-2.42']])
-
-    def test_positive(self):
-        self.assertEqual(
-            self.export(47.36),
-            [[u'47.36']])
-
-    def test_big(self):
-        self.assertEqual(
-            self.export(87654321.4678),
-            [[u'87654321.4678']])
-
-class test_decimal_field(CreatorCase):
-    model_name = 'export.decimal'
-
-    def test_0(self):
-        self.assertEqual(
-            self.export(0.0),
-            [[False]])
-
-    def test_epsilon(self):
-        """ epsilon gets sliced to 0 due to precision
-        """
-        self.assertEqual(
-            self.export(0.000000000027),
-            [[False]])
-
-    def test_negative(self):
-        self.assertEqual(
-            self.export(-2.42),
-            [[u'-2.42']])
-
-    def test_positive(self):
-        self.assertEqual(
-            self.export(47.36),
-            [[u'47.36']])
-
-    def test_big(self):
-        self.assertEqual(
-            self.export(87654321.4678), [[u'87654321.468']])
-
-class test_string_field(CreatorCase):
-    model_name = 'export.string.bounded'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.export(""),
-            [[False]])
-    def test_within_bounds(self):
-        self.assertEqual(
-            self.export("foobar"),
-            [[u"foobar"]])
-    def test_out_of_bounds(self):
-        self.assertEqual(
-            self.export("C for Sinking, "
-                        "Java for Drinking, "
-                        "Smalltalk for Thinking. "
-                        "...and Power to the Penguin!"),
-            [[u"C for Sinking, J"]])
-
-class test_unbound_string_field(CreatorCase):
-    model_name = 'export.string'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.export(""),
-            [[False]])
-    def test_small(self):
-        self.assertEqual(
-            self.export("foobar"),
-            [[u"foobar"]])
-    def test_big(self):
-        self.assertEqual(
-            self.export("We flew down weekly to meet with IBM, but they "
-                        "thought the way to measure software was the amount "
-                        "of code we wrote, when really the better the "
-                        "software, the fewer lines of code."),
-            [[u"We flew down weekly to meet with IBM, but they thought the "
-              u"way to measure software was the amount of code we wrote, "
-              u"when really the better the software, the fewer lines of "
-              u"code."]])
-
-class test_text(CreatorCase):
-    model_name = 'export.text'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.export(""),
-            [[False]])
-    def test_small(self):
-        self.assertEqual(
-            self.export("foobar"),
-            [[u"foobar"]])
-    def test_big(self):
-        self.assertEqual(
-            self.export("So, `bind' is `let' and monadic programming is"
-                        " equivalent to programming in the A-normal form. That"
-                        " is indeed all there is to monads"),
-            [[u"So, `bind' is `let' and monadic programming is equivalent to"
-              u" programming in the A-normal form. That is indeed all there"
-              u" is to monads"]])
-
-class test_date(CreatorCase):
-    model_name = 'export.date'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.export(False),
-            [[False]])
-    def test_basic(self):
-        self.assertEqual(
-            self.export('2011-11-07'),
-            [[u'2011-11-07']])
-
-class test_datetime(CreatorCase):
-    model_name = 'export.datetime'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.export(False),
-            [[False]])
-    def test_basic(self):
-        self.assertEqual(
-            self.export('2011-11-07 21:05:48'),
-            [[u'2011-11-07 21:05:48']])
-    def test_tz(self):
-        """ Export ignores the timezone and always exports to UTC
-
-        .. note:: on the other hand, export uses user lang for name_get
-        """
-        # NOTE: ignores user timezone, always exports to UTC
-        self.assertEqual(
-            self.export('2011-11-07 21:05:48', context={'tz': 'Pacific/Norfolk'}),
-            [[u'2011-11-07 21:05:48']])
-
-class test_selection(CreatorCase):
-    model_name = 'export.selection'
-    translations_fr = [
-        ("Qux", "toto"),
-        ("Bar", "titi"),
-        ("Foo", "tete"),
-    ]
-
-    def test_empty(self):
-        self.assertEqual(
-            self.export(False),
-            [[False]])
-
-    def test_value(self):
-        """ selections export the *label* for their value
-        """
-        self.assertEqual(
-            self.export(2),
-            [[u"Bar"]])
-
-    def test_localized_export(self):
-        self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, {
-            'name': u'Français',
-            'code': 'fr_FR',
-            'translatable': True,
-            'date_format': '%d.%m.%Y',
-            'decimal_point': ',',
-            'thousands_sep': ' ',
-        })
-        Translations = self.registry('ir.translation')
-        for source, value in self.translations_fr:
-            Translations.create(self.cr, openerp.SUPERUSER_ID, {
-                'name': 'export.selection,value',
-                'lang': 'fr_FR',
-                'type': 'selection',
-                'src': source,
-                'value': value
-            })
-        self.assertEqual(
-            self.export(2, context={'lang': 'fr_FR'}),
-            [[u'Bar']])
-
-class test_selection_function(CreatorCase):
-    model_name = 'export.selection.function'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.export(False),
-            [[False]])
-
-    def test_value(self):
-        # FIXME: selection functions export the *value* itself
-        self.assertEqual(
-            self.export(1),
-            [[u'1']])
-        self.assertEqual(
-            self.export(3),
-            [[u'3']])
-        # fucking hell
-        self.assertEqual(
-            self.export(0),
-            [[False]])
-
-class test_m2o(CreatorCase):
-    model_name = 'export.many2one'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.export(False),
-            [[False]])
-    def test_basic(self):
-        """ Exported value is the name_get of the related object
-        """
-        integer_id = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        name = dict(self.registry('export.integer').name_get(
-            self.cr, openerp.SUPERUSER_ID,[integer_id]))[integer_id]
-        self.assertEqual(
-            self.export(integer_id),
-            [[name]])
-    def test_path(self):
-        """ Can recursively export fields of m2o via path
-        """
-        integer_id = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        self.assertEqual(
-            self.export(integer_id, fields=['value/.id', 'value/value']),
-            [[unicode(integer_id), u'42']])
-    def test_external_id(self):
-        integer_id = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        # __export__.$class.$id
-        external_id = u'__export__.export_many2one_%d' % integer_id
-        self.assertEqual(
-            self.export(integer_id, fields=['value/id']),
-            [[external_id]])
-
-class test_o2m(CreatorCase):
-    model_name = 'export.one2many'
-    commands = [
-        (0, False, {'value': 4, 'str': 'record1'}),
-        (0, False, {'value': 42, 'str': 'record2'}),
-        (0, False, {'value': 36, 'str': 'record3'}),
-        (0, False, {'value': 4, 'str': 'record4'}),
-        (0, False, {'value': 13, 'str': 'record5'}),
-    ]
-    names = [
-        u'export.one2many.child:%d' % d['value']
-        for c, _, d in commands
-    ]
-
-    def test_empty(self):
-        self.assertEqual(
-            self.export(False),
-            [[False]])
-
-    def test_single(self):
-        self.assertEqual(
-            self.export([(0, False, {'value': 42})]),
-            # name_get result
-            [[u'export.one2many.child:42']])
-
-    def test_single_subfield(self):
-        self.assertEqual(
-            self.export([(0, False, {'value': 42})],
-                        fields=['value', 'value/value']),
-            [[u'export.one2many.child:42', u'42']])
-
-    def test_integrate_one_in_parent(self):
-        self.assertEqual(
-            self.export([(0, False, {'value': 42})],
-                        fields=['const', 'value/value']),
-            [[u'4', u'42']])
-
-    def test_multiple_records(self):
-        self.assertEqual(
-            self.export(self.commands, fields=['const', 'value/value']),
-            [
-                [u'4', u'4'],
-                [u'', u'42'],
-                [u'', u'36'],
-                [u'', u'4'],
-                [u'', u'13'],
-            ])
-
-    def test_multiple_records_name(self):
-        self.assertEqual(
-            self.export(self.commands, fields=['const', 'value']),
-            [[
-                u'4', u','.join(self.names)
-            ]])
-
-    def test_multiple_records_id(self):
-        export = self.export(self.commands, fields=['const', 'value/.id'])
-        O2M_c = self.registry('export.one2many.child')
-        ids = O2M_c.browse(self.cr, openerp.SUPERUSER_ID,
-                           O2M_c.search(self.cr, openerp.SUPERUSER_ID, []))
-        self.assertEqual(
-            export,
-            [
-                ['4', str(ids[0].id)],
-                ['', str(ids[1].id)],
-                ['', str(ids[2].id)],
-                ['', str(ids[3].id)],
-                ['', str(ids[4].id)],
-            ])
-
-    def test_multiple_records_with_name_before(self):
-        self.assertEqual(
-            self.export(self.commands, fields=['const', 'value', 'value/value']),
-            [[ # exports sub-fields of very first o2m
-                u'4', u','.join(self.names), u'4'
-            ]])
-
-    def test_multiple_records_with_name_after(self):
-        self.assertEqual(
-            self.export(self.commands, fields=['const', 'value/value', 'value']),
-            [ # completely ignores name_get request
-                [u'4', u'4', ''],
-                ['', u'42', ''],
-                ['', u'36', ''],
-                ['', u'4', ''],
-                ['', u'13', ''],
-            ])
-
-    def test_multiple_subfields_neighbour(self):
-        self.assertEqual(
-            self.export(self.commands, fields=['const', 'value/str','value/value']),
-            [
-                [u'4', u'record1', u'4'],
-                ['', u'record2', u'42'],
-                ['', u'record3', u'36'],
-                ['', u'record4', u'4'],
-                ['', u'record5', u'13'],
-            ])
-
-    def test_multiple_subfields_separated(self):
-        self.assertEqual(
-            self.export(self.commands, fields=['value/str', 'const', 'value/value']),
-            [
-                [u'record1', u'4', u'4'],
-                [u'record2', '', u'42'],
-                [u'record3', '', u'36'],
-                [u'record4', '', u'4'],
-                [u'record5', '', u'13'],
-            ])
-
-class test_o2m_multiple(CreatorCase):
-    model_name = 'export.one2many.multiple'
-
-    def make(self, value=None, **values):
-        if value is not None: values['value'] = value
-        id = self.model.create(self.cr, openerp.SUPERUSER_ID, values)
-        return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0]
-    def export(self, value=None, fields=('child1', 'child2',), context=None, **values):
-        record = self.make(value, **values)
-        return self.model._BaseModel__export_row(
-            self.cr, openerp.SUPERUSER_ID, record,
-            [f.split('/') for f in fields],
-            context=context)
-
-    def test_empty(self):
-        self.assertEqual(
-            self.export(child1=False, child2=False),
-            [[False, False]])
-
-    def test_single_per_side(self):
-        self.assertEqual(
-            self.export(child1=False, child2=[(0, False, {'value': 42})]),
-            [[False, u'export.one2many.child.2:42']])
-
-        self.assertEqual(
-            self.export(child1=[(0, False, {'value': 43})], child2=False),
-            [[u'export.one2many.child.1:43', False]])
-
-        self.assertEqual(
-            self.export(child1=[(0, False, {'value': 43})],
-                        child2=[(0, False, {'value': 42})]),
-            [[u'export.one2many.child.1:43', u'export.one2many.child.2:42']])
-
-    def test_single_integrate_subfield(self):
-        fields = ['const', 'child1/value', 'child2/value']
-        self.assertEqual(
-            self.export(child1=False, child2=[(0, False, {'value': 42})],
-                        fields=fields),
-            [[u'36', False, u'42']])
-
-        self.assertEqual(
-            self.export(child1=[(0, False, {'value': 43})], child2=False,
-                        fields=fields),
-            [[u'36', u'43', False]])
-
-        self.assertEqual(
-            self.export(child1=[(0, False, {'value': 43})],
-                        child2=[(0, False, {'value': 42})],
-                        fields=fields),
-            [[u'36', u'43', u'42']])
-
-    def test_multiple(self):
-        """ With two "concurrent" o2ms, exports the first line combined, then
-        exports the rows for the first o2m, then the rows for the second o2m.
-        """
-        fields = ['const', 'child1/value', 'child2/value']
-        child1 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
-                  for index, v in zip(itertools.count(), [4, 42, 36, 4, 13])]
-        child2 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
-                  for index, v in zip(itertools.count(10), [8, 12, 8, 55, 33, 13])]
-
-        self.assertEqual(
-            self.export(child1=child1, child2=False, fields=fields),
-            [
-                [u'36', u'4', False],
-                ['', u'42', ''],
-                ['', u'36', ''],
-                ['', u'4', ''],
-                ['', u'13', ''],
-            ])
-        self.assertEqual(
-            self.export(child1=False, child2=child2, fields=fields),
-            [
-                [u'36', False, u'8'],
-                ['', '', u'12'],
-                ['', '', u'8'],
-                ['', '', u'55'],
-                ['', '', u'33'],
-                ['', '', u'13'],
-            ])
-        self.assertEqual(
-            self.export(child1=child1, child2=child2, fields=fields),
-            [
-                [u'36', u'4', u'8'],
-                ['', u'42', ''],
-                ['', u'36', ''],
-                ['', u'4', ''],
-                ['', u'13', ''],
-                ['', '', u'12'],
-                ['', '', u'8'],
-                ['', '', u'55'],
-                ['', '', u'33'],
-                ['', '', u'13'],
-            ])
-
-class test_m2m(CreatorCase):
-    model_name = 'export.many2many'
-    commands = [
-        (0, False, {'value': 4, 'str': 'record000'}),
-        (0, False, {'value': 42, 'str': 'record001'}),
-        (0, False, {'value': 36, 'str': 'record010'}),
-        (0, False, {'value': 4, 'str': 'record011'}),
-        (0, False, {'value': 13, 'str': 'record100'}),
-    ]
-    names = [
-        u'export.many2many.other:%d' % d['value']
-        for c, _, d in commands
-    ]
-
-    def test_empty(self):
-        self.assertEqual(
-            self.export(False),
-            [[False]])
-
-    def test_single(self):
-        self.assertEqual(
-            self.export([(0, False, {'value': 42})]),
-            # name_get result
-            [[u'export.many2many.other:42']])
-
-    def test_single_subfield(self):
-        self.assertEqual(
-            self.export([(0, False, {'value': 42})],
-                        fields=['value', 'value/value']),
-            [[u'export.many2many.other:42', u'42']])
-
-    def test_integrate_one_in_parent(self):
-        self.assertEqual(
-            self.export([(0, False, {'value': 42})],
-                        fields=['const', 'value/value']),
-            [[u'4', u'42']])
-
-    def test_multiple_records(self):
-        self.assertEqual(
-            self.export(self.commands, fields=['const', 'value/value']),
-            [
-                [u'4', u'4'],
-                [u'', u'42'],
-                [u'', u'36'],
-                [u'', u'4'],
-                [u'', u'13'],
-            ])
-
-    def test_multiple_records_name(self):
-        self.assertEqual(
-            self.export(self.commands, fields=['const', 'value']),
-            [[ # FIXME: hardcoded comma, import uses config.csv_internal_sep
-               # resolution: remove configurable csv_internal_sep
-                u'4', u','.join(self.names)
-            ]])
-
-    # essentially same as o2m, so boring
-
-class test_function(CreatorCase):
-    model_name = 'export.function'
-
-    def test_value(self):
-        """ Exports value normally returned by accessing the function field
-        """
-        self.assertEqual(
-            self.export(42),
-            [[u'3']])
diff --git a/openerp/tests/addons/test_impex/tests/test_import.py b/openerp/tests/addons/test_impex/tests/test_import.py
deleted file mode 100644 (file)
index e4e8a6a..0000000
+++ /dev/null
@@ -1,882 +0,0 @@
-# -*- coding: utf-8 -*-
-import openerp.modules.registry
-import openerp
-
-from openerp.tests import common
-from openerp.tools.misc import mute_logger
-
-def ok(n):
-    """ Successful import of ``n`` records
-
-    :param int n: number of records which should have been imported
-    """
-    return n, 0, 0, 0
-
-def error(row, message, record=None, **kwargs):
-    """ Failed import of the record ``record`` at line ``row``, with the error
-    message ``message``
-
-    :param str message:
-    :param dict record:
-    """
-    return (
-        -1, dict(record or {}, **kwargs),
-        "Line %d : %s" % (row, message),
-        '')
-
-def values(seq, field='value'):
-    return [item[field] for item in seq]
-
-class ImporterCase(common.TransactionCase):
-    model_name = False
-
-    def __init__(self, *args, **kwargs):
-        super(ImporterCase, self).__init__(*args, **kwargs)
-        self.model = None
-
-    def setUp(self):
-        super(ImporterCase, self).setUp()
-        self.model = self.registry(self.model_name)
-
-    def import_(self, fields, rows, context=None):
-        return self.model.import_data(
-            self.cr, openerp.SUPERUSER_ID, fields, rows, context=context)
-    def read(self, fields=('value',), domain=(), context=None):
-        return self.model.read(
-            self.cr, openerp.SUPERUSER_ID,
-            self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context),
-            fields=fields, context=context)
-    def browse(self, domain=(), context=None):
-        return self.model.browse(
-            self.cr, openerp.SUPERUSER_ID,
-            self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context),
-            context=context)
-
-    def xid(self, record):
-        ModelData = self.registry('ir.model.data')
-
-        ids = ModelData.search(
-            self.cr, openerp.SUPERUSER_ID,
-            [('model', '=', record._table_name), ('res_id', '=', record.id)])
-        if ids:
-            d = ModelData.read(
-                self.cr, openerp.SUPERUSER_ID, ids, ['name', 'module'])[0]
-            if d['module']:
-                return '%s.%s' % (d['module'], d['name'])
-            return d['name']
-
-        name = dict(record.name_get())[record.id]
-        # fix dotted name_get results, otherwise xid lookups blow up
-        name = name.replace('.', '-')
-        ModelData.create(self.cr, openerp.SUPERUSER_ID, {
-            'name': name,
-            'model': record._table_name,
-            'res_id': record.id,
-            'module': '__test__'
-        })
-        return '__test__.' + name
-
-class test_ids_stuff(ImporterCase):
-    model_name = 'export.integer'
-
-    def test_create_with_id(self):
-        self.assertEqual(
-            self.import_(['.id', 'value'], [['42', '36']]),
-            error(1, u"Unknown database identifier '42'"))
-    def test_create_with_xid(self):
-        self.assertEqual(
-            self.import_(['id', 'value'], [['somexmlid', '42']]),
-            ok(1))
-        self.assertEqual(
-            'somexmlid',
-            self.xid(self.browse()[0]))
-
-    def test_update_with_id(self):
-        id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': 36})
-        self.assertEqual(
-            36,
-            self.model.browse(self.cr, openerp.SUPERUSER_ID, id).value)
-
-        self.assertEqual(
-            self.import_(['.id', 'value'], [[str(id), '42']]),
-            ok(1))
-        self.assertEqual(
-            [42], # updated value to imported
-            values(self.read()))
-
-    def test_update_with_xid(self):
-        self.import_(['id', 'value'], [['somexmlid', '36']])
-        self.assertEqual([36], values(self.read()))
-
-        self.import_(['id', 'value'], [['somexmlid', '1234567']])
-        self.assertEqual([1234567], values(self.read()))
-
-class test_boolean_field(ImporterCase):
-    model_name = 'export.boolean'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.import_(['value'], []),
-            ok(0))
-
-    def test_exported(self):
-        self.assertEqual(
-            self.import_(['value'], [
-                ['False'],
-                ['True'],
-            ]),
-            ok(2))
-        records = self.read()
-        self.assertEqual([
-            False,
-            True,
-        ], values(records))
-
-    def test_falses(self):
-        self.assertEqual(
-            self.import_(['value'], [
-                [u'0'],
-                [u'no'],
-                [u'false'],
-                [u'FALSE'],
-                [u''],
-            ]),
-            ok(5))
-        self.assertEqual([
-                False,
-                False,
-                False,
-                False,
-                False,
-            ],
-            values(self.read()))
-
-    def test_trues(self):
-        self.assertEqual(
-            self.import_(['value'], [
-                ['off'],
-                ['None'],
-                ['nil'],
-                ['()'],
-                ['f'],
-                ['#f'],
-                # Problem: OpenOffice (and probably excel) output localized booleans
-                ['VRAI'],
-                [u'OFF'],
-            ]),
-            ok(8))
-        self.assertEqual(
-            [True] * 8,
-            values(self.read()))
-
-class test_integer_field(ImporterCase):
-    model_name = 'export.integer'
-
-    def test_none(self):
-        self.assertEqual(
-            self.import_(['value'], []),
-            ok(0))
-
-    def test_empty(self):
-        self.assertEqual(
-            self.import_(['value'], [['']]),
-            ok(1))
-        self.assertEqual(
-            [False],
-            values(self.read()))
-
-    def test_zero(self):
-        self.assertEqual(
-            self.import_(['value'], [['0']]),
-            ok(1))
-        self.assertEqual(
-            self.import_(['value'], [['-0']]),
-            ok(1))
-        self.assertEqual([False, False], values(self.read()))
-
-    def test_positives(self):
-        self.assertEqual(
-            self.import_(['value'], [
-                ['1'],
-                ['42'],
-                [str(2**31-1)],
-                ['12345678']
-            ]),
-            ok(4))
-        self.assertEqual([
-            1, 42, 2**31-1, 12345678
-        ], values(self.read()))
-
-    def test_negatives(self):
-        self.assertEqual(
-            self.import_(['value'], [
-                ['-1'],
-                ['-42'],
-                [str(-(2**31 - 1))],
-                [str(-(2**31))],
-                ['-12345678']
-            ]),
-            ok(5))
-        self.assertEqual([
-            -1, -42, -(2**31 - 1), -(2**31), -12345678
-        ], values(self.read()))
-
-    @mute_logger('openerp.sql_db')
-    def test_out_of_range(self):
-        self.assertEqual(
-            self.import_(['value'], [[str(2**31)]]),
-            error(1, "integer out of range\n"))
-        # auto-rollbacks if error is in process_liness, but not during
-        # ir.model.data write. Can differentiate because former ends lines
-        # error lines with "!"
-        self.cr.rollback()
-        self.assertEqual(
-            self.import_(['value'], [[str(-2**32)]]),
-            error(1, "integer out of range\n"))
-
-
-    def test_nonsense(self):
-        self.assertEqual(
-            self.import_(['value'], [['zorglub']]),
-            error(1, u"'zorglub' does not seem to be an integer for field 'unknown'"))
-
-class test_float_field(ImporterCase):
-    model_name = 'export.float'
-    def test_none(self):
-        self.assertEqual(
-            self.import_(['value'], []),
-            ok(0))
-
-    def test_empty(self):
-        self.assertEqual(
-            self.import_(['value'], [['']]),
-            ok(1))
-        self.assertEqual(
-            [False],
-            values(self.read()))
-
-    def test_zero(self):
-        self.assertEqual(
-            self.import_(['value'], [['0']]),
-            ok(1))
-        self.assertEqual(
-            self.import_(['value'], [['-0']]),
-            ok(1))
-        self.assertEqual([False, False], values(self.read()))
-
-    def test_positives(self):
-        self.assertEqual(
-            self.import_(['value'], [
-                ['1'],
-                ['42'],
-                [str(2**31-1)],
-                ['12345678'],
-                [str(2**33)],
-                ['0.000001'],
-            ]),
-            ok(6))
-        self.assertEqual([
-            1, 42, 2**31-1, 12345678, 2.0**33, .000001
-        ], values(self.read()))
-
-    def test_negatives(self):
-        self.assertEqual(
-            self.import_(['value'], [
-                ['-1'],
-                ['-42'],
-                [str(-2**31 + 1)],
-                [str(-2**31)],
-                ['-12345678'],
-                [str(-2**33)],
-                ['-0.000001'],
-            ]),
-            ok(7))
-        self.assertEqual([
-            -1, -42, -(2**31 - 1), -(2**31), -12345678, -2.0**33, -.000001
-        ], values(self.read()))
-
-    def test_nonsense(self):
-        self.assertEqual(
-            self.import_(['value'], [['foobar']]),
-            error(1, u"'foobar' does not seem to be a number for field 'unknown'"))
-
-class test_string_field(ImporterCase):
-    model_name = 'export.string.bounded'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.import_(['value'], [['']]),
-            ok(1))
-        self.assertEqual([False], values(self.read()))
-
-    def test_imported(self):
-        self.assertEqual(
-            self.import_(['value'], [
-                [u'foobar'],
-                [u'foobarbaz'],
-                [u'Með suð í eyrum við spilum endalaust'],
-                [u"People 'get' types. They use them all the time. Telling "
-                 u"someone he can't pound a nail with a banana doesn't much "
-                 u"surprise him."]
-            ]),
-            ok(4))
-        self.assertEqual([
-            u"foobar",
-            u"foobarbaz",
-            u"Með suð í eyrum ",
-            u"People 'get' typ",
-        ], values(self.read()))
-
-class test_unbound_string_field(ImporterCase):
-    model_name = 'export.string'
-
-    def test_imported(self):
-        self.assertEqual(
-            self.import_(['value'], [
-                [u'í dag viðrar vel til loftárása'],
-                # ackbar.jpg
-                [u"If they ask you about fun, you tell them – fun is a filthy"
-                 u" parasite"]
-            ]),
-            ok(2))
-        self.assertEqual([
-            u"í dag viðrar vel til loftárása",
-            u"If they ask you about fun, you tell them – fun is a filthy parasite"
-        ], values(self.read()))
-
-class test_text(ImporterCase):
-    model_name = 'export.text'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.import_(['value'], [['']]),
-            ok(1))
-        self.assertEqual([False], values(self.read()))
-
-    def test_imported(self):
-        s = (u"Breiðskífa er notað um útgefna hljómplötu sem inniheldur "
-             u"stúdíóupptökur frá einum flytjanda. Breiðskífur eru oftast "
-             u"milli 25-80 mínútur og er lengd þeirra oft miðuð við 33⅓ "
-             u"snúninga 12 tommu vínylplötur (sem geta verið allt að 30 mín "
-             u"hvor hlið).\n\nBreiðskífur eru stundum tvöfaldar og eru þær þá"
-             u" gefnar út á tveimur geisladiskum eða tveimur vínylplötum.")
-        self.assertEqual(
-            self.import_(['value'], [[s]]),
-            ok(1))
-        self.assertEqual([s], values(self.read()))
-
-class test_selection(ImporterCase):
-    model_name = 'export.selection'
-    translations_fr = [
-        ("Qux", "toto"),
-        ("Bar", "titi"),
-        ("Foo", "tete"),
-    ]
-
-    def test_imported(self):
-        self.assertEqual(
-            self.import_(['value'], [
-                ['Qux'],
-                ['Bar'],
-                ['Foo'],
-                ['2'],
-            ]),
-            ok(4))
-        self.assertEqual([3, 2, 1, 2], values(self.read()))
-
-    def test_imported_translated(self):
-        self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, {
-            'name': u'Français',
-            'code': 'fr_FR',
-            'translatable': True,
-            'date_format': '%d.%m.%Y',
-            'decimal_point': ',',
-            'thousands_sep': ' ',
-        })
-        Translations = self.registry('ir.translation')
-        for source, value in self.translations_fr:
-            Translations.create(self.cr, openerp.SUPERUSER_ID, {
-                'name': 'export.selection,value',
-                'lang': 'fr_FR',
-                'type': 'selection',
-                'src': source,
-                'value': value
-            })
-
-        self.assertEqual(
-            self.import_(['value'], [
-                ['toto'],
-                ['tete'],
-                ['titi'],
-            ], context={'lang': 'fr_FR'}),
-            ok(3))
-        self.assertEqual([3, 1, 2], values(self.read()))
-        self.assertEqual(
-            self.import_(['value'], [['Foo']], context={'lang': 'fr_FR'}),
-            ok(1))
-
-    def test_invalid(self):
-        self.assertEqual(
-            self.import_(['value'], [['Baz']]),
-            error(1, u"Value 'Baz' not found in selection field 'unknown'"))
-        self.cr.rollback()
-        self.assertEqual(
-            self.import_(['value'], [[42]]),
-            error(1, u"Value '42' not found in selection field 'unknown'"))
-
-class test_selection_function(ImporterCase):
-    model_name = 'export.selection.function'
-    translations_fr = [
-        ("Corge", "toto"),
-        ("Grault", "titi"),
-        ("Wheee", "tete"),
-        ("Moog", "tutu"),
-    ]
-
-    def test_imported(self):
-        """ import uses fields_get, so translates import label (may or may not
-        be good news) *and* serializes the selection function to reverse it:
-        import does not actually know that the selection field uses a function
-        """
-        # NOTE: conflict between a value and a label => ?
-        self.assertEqual(
-            self.import_(['value'], [
-                ['3'],
-                ["Grault"],
-            ]),
-            ok(2))
-        self.assertEqual(
-            ['3', '1'],
-            values(self.read()))
-
-    def test_translated(self):
-        """ Expects output of selection function returns translated labels
-        """
-        self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, {
-            'name': u'Français',
-            'code': 'fr_FR',
-            'translatable': True,
-            'date_format': '%d.%m.%Y',
-            'decimal_point': ',',
-            'thousands_sep': ' ',
-        })
-        Translations = self.registry('ir.translation')
-        for source, value in self.translations_fr:
-            Translations.create(self.cr, openerp.SUPERUSER_ID, {
-                'name': 'export.selection,value',
-                'lang': 'fr_FR',
-                'type': 'selection',
-                'src': source,
-                'value': value
-            })
-        self.assertEqual(
-            self.import_(['value'], [
-                ['toto'],
-                ['tete'],
-            ], context={'lang': 'fr_FR'}),
-            ok(2))
-        self.assertEqual(
-            self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'}),
-            ok(1))
-
-class test_m2o(ImporterCase):
-    model_name = 'export.many2one'
-
-    def test_by_name(self):
-        # create integer objects
-        integer_id1 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        integer_id2 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 36})
-        # get its name
-        name1 = dict(self.registry('export.integer').name_get(
-            self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1]
-        name2 = dict(self.registry('export.integer').name_get(
-            self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2]
-
-        self.assertEqual(
-            self.import_(['value'], [
-                # import by name_get
-                [name1],
-                [name1],
-                [name2],
-            ]),
-            ok(3))
-        # correct ids assigned to corresponding records
-        self.assertEqual([
-            (integer_id1, name1),
-            (integer_id1, name1),
-            (integer_id2, name2),],
-            values(self.read()))
-
-    def test_by_xid(self):
-        ExportInteger = self.registry('export.integer')
-        integer_id = ExportInteger.create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        xid = self.xid(ExportInteger.browse(
-            self.cr, openerp.SUPERUSER_ID, [integer_id])[0])
-
-        self.assertEqual(
-            self.import_(['value/id'], [[xid]]),
-            ok(1))
-        b = self.browse()
-        self.assertEqual(42, b[0].value.value)
-
-    def test_by_id(self):
-        integer_id = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        self.assertEqual(
-            self.import_(['value/.id'], [[integer_id]]),
-            ok(1))
-        b = self.browse()
-        self.assertEqual(42, b[0].value.value)
-
-    def test_by_names(self):
-        integer_id1 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        integer_id2 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        name1 = dict(self.registry('export.integer').name_get(
-            self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1]
-        name2 = dict(self.registry('export.integer').name_get(
-            self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2]
-        # names should be the same
-        self.assertEqual(name1, name2)
-
-        self.assertEqual(
-            self.import_(['value'], [[name2]]),
-            ok(1))
-        self.assertEqual([
-            (integer_id1, name1)
-        ], values(self.read()))
-
-    def test_fail_by_implicit_id(self):
-        """ Can't implicitly import records by id
-        """
-        # create integer objects
-        integer_id1 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        integer_id2 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 36})
-
-        self.assertEqual(
-            self.import_(['value'], [
-                # import by id, without specifying it
-                [integer_id1],
-                [integer_id2],
-                [integer_id1],
-            ]),
-            error(1, u"No matching record found for name '%s' in field 'unknown'" % integer_id1))
-
-    def test_sub_field(self):
-        """ Does not implicitly create the record, does not warn that you can't
-        import m2o subfields (at all)...
-        """
-        self.assertEqual(
-            self.import_(['value/value'], [['42']]),
-            error(1, u"Can not create Many-To-One records indirectly, import the field separately"))
-
-    def test_fail_noids(self):
-        self.assertEqual(
-            self.import_(['value'], [['nameisnoexist:3']]),
-            error(1, u"No matching record found for name 'nameisnoexist:3' in field 'unknown'"))
-        self.cr.rollback()
-        self.assertEqual(
-            self.import_(['value/id'], [['noxidhere']]),
-            error(1, u"No matching record found for external id 'noxidhere' in field 'unknown'"))
-        self.cr.rollback()
-        self.assertEqual(
-            self.import_(['value/.id'], [[66]]),
-            error(1, u"No matching record found for database id '66' in field 'unknown'"))
-
-class test_m2m(ImporterCase):
-    model_name = 'export.many2many'
-
-    # apparently, one and only thing which works is a
-    # csv_internal_sep-separated list of ids, xids, or names (depending if
-    # m2m/.id, m2m/id or m2m[/anythingelse]
-    def test_ids(self):
-        id1 = self.registry('export.many2many.other').create(
-                self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
-        id2 = self.registry('export.many2many.other').create(
-                self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
-        id3 = self.registry('export.many2many.other').create(
-                self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
-        id4 = self.registry('export.many2many.other').create(
-                self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
-        id5 = self.registry('export.many2many.other').create(
-                self.cr, openerp.SUPERUSER_ID, {'value': 99, 'str': 'record4'})
-
-        self.assertEqual(
-            self.import_(['value/.id'], [
-                ['%d,%d' % (id1, id2)],
-                ['%d,%d,%d' % (id1, id3, id4)],
-                ['%d,%d,%d' % (id1, id2, id3)],
-                ['%d' % id5]
-            ]),
-            ok(4))
-        ids = lambda records: [record.id for record in records]
-
-        b = self.browse()
-        self.assertEqual(ids(b[0].value), [id1, id2])
-        self.assertEqual(values(b[0].value), [3, 44])
-
-        self.assertEqual(ids(b[2].value), [id1, id2, id3])
-        self.assertEqual(values(b[2].value), [3, 44, 84])
-
-    def test_noids(self):
-        self.assertEqual(
-            self.import_(['value/.id'], [['42']]),
-            error(1, u"No matching record found for database id '42' in field 'unknown'"))
-
-    def test_xids(self):
-        M2O_o = self.registry('export.many2many.other')
-        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
-        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
-        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
-        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
-        records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4])
-
-        self.assertEqual(
-            self.import_(['value/id'], [
-                ['%s,%s' % (self.xid(records[0]), self.xid(records[1]))],
-                ['%s' % self.xid(records[3])],
-                ['%s,%s' % (self.xid(records[2]), self.xid(records[1]))],
-            ]),
-            ok(3))
-
-        b = self.browse()
-        self.assertEqual(values(b[0].value), [3, 44])
-        self.assertEqual(values(b[2].value), [44, 84])
-    def test_noxids(self):
-        self.assertEqual(
-            self.import_(['value/id'], [['noxidforthat']]),
-            error(1, u"No matching record found for external id 'noxidforthat' in field 'unknown'"))
-
-    def test_names(self):
-        M2O_o = self.registry('export.many2many.other')
-        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
-        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
-        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
-        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
-        records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4])
-
-        name = lambda record: dict(record.name_get())[record.id]
-
-        self.assertEqual(
-            self.import_(['value'], [
-                ['%s,%s' % (name(records[1]), name(records[2]))],
-                ['%s,%s,%s' % (name(records[0]), name(records[1]), name(records[2]))],
-                ['%s,%s' % (name(records[0]), name(records[3]))],
-            ]),
-            ok(3))
-
-        b = self.browse()
-        self.assertEqual(values(b[1].value), [3, 44, 84])
-        self.assertEqual(values(b[2].value), [3, 9])
-
-    def test_nonames(self):
-        self.assertEqual(
-            self.import_(['value'], [['wherethem2mhavenonames']]),
-            error(1, u"No matching record found for name 'wherethem2mhavenonames' in field 'unknown'"))
-
-    def test_import_to_existing(self):
-        M2O_o = self.registry('export.many2many.other')
-        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
-        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
-        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
-        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
-
-        xid = 'myxid'
-        self.assertEqual(
-            self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id1, id2)]]),
-            ok(1))
-        self.assertEqual(
-            self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id3, id4)]]),
-            ok(1))
-
-        b = self.browse()
-        self.assertEqual(len(b), 1)
-        # TODO: replacement of existing m2m values is correct?
-        self.assertEqual(values(b[0].value), [84, 9])
-
-class test_o2m(ImporterCase):
-    model_name = 'export.one2many'
-
-    def test_name_get(self):
-        s = u'Java is a DSL for taking large XML files and converting them to' \
-            u' stack traces'
-        self.assertEqual(
-            self.import_(
-                ['const', 'value'],
-                [['5', s]]),
-            error(1, u"No matching record found for name '%s' in field 'unknown'" % s))
-
-    def test_single(self):
-        self.assertEqual(
-            self.import_(['const', 'value/value'], [
-                ['5', '63']
-            ]),
-            ok(1))
-
-        (b,) = self.browse()
-        self.assertEqual(b.const, 5)
-        self.assertEqual(values(b.value), [63])
-
-    def test_multicore(self):
-        self.assertEqual(
-            self.import_(['const', 'value/value'], [
-                ['5', '63'],
-                ['6', '64'],
-            ]),
-            ok(2))
-
-        b1, b2 = self.browse()
-        self.assertEqual(b1.const, 5)
-        self.assertEqual(values(b1.value), [63])
-        self.assertEqual(b2.const, 6)
-        self.assertEqual(values(b2.value), [64])
-
-    def test_multisub(self):
-        self.assertEqual(
-            self.import_(['const', 'value/value'], [
-                ['5', '63'],
-                ['', '64'],
-                ['', '65'],
-                ['', '66'],
-            ]),
-            ok(4))
-
-        (b,) = self.browse()
-        self.assertEqual(values(b.value), [63, 64, 65, 66])
-
-    def test_multi_subfields(self):
-        self.assertEqual(
-            self.import_(['value/str', 'const', 'value/value'], [
-                ['this', '5', '63'],
-                ['is', '', '64'],
-                ['the', '', '65'],
-                ['rhythm', '', '66'],
-            ]),
-            ok(4))
-
-        (b,) = self.browse()
-        self.assertEqual(values(b.value), [63, 64, 65, 66])
-        self.assertEqual(
-            values(b.value, 'str'),
-            'this is the rhythm'.split())
-
-    def test_link_inline(self):
-        id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Bf', 'value': 109
-        })
-        id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Me', 'value': 262
-        })
-
-        try:
-            self.import_(['const', 'value/.id'], [
-                ['42', '%d,%d' % (id1, id2)]
-            ])
-        except ValueError, e:
-            # should be Exception(Database ID doesn't exist: export.one2many.child : $id1,$id2)
-            self.assertIs(type(e), ValueError)
-            self.assertEqual(
-                e.args[0],
-                "invalid literal for int() with base 10: '%d,%d'" % (id1, id2))
-
-    def test_link(self):
-        id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Bf', 'value': 109
-        })
-        id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Me', 'value': 262
-        })
-
-        self.assertEqual(
-            self.import_(['const', 'value/.id'], [
-                ['42', str(id1)],
-                ['', str(id2)],
-            ]),
-            ok(2))
-
-        [b] = self.browse()
-        self.assertEqual(b.const, 42)
-        # automatically forces link between core record and o2ms
-        self.assertEqual(values(b.value), [109, 262])
-        self.assertEqual(values(b.value, field='parent_id'), [b, b])
-
-    def test_link_2(self):
-        O2M_c = self.registry('export.one2many.child')
-        id1 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Bf', 'value': 109
-        })
-        id2 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Me', 'value': 262
-        })
-
-        self.assertEqual(
-            self.import_(['const', 'value/.id', 'value/value'], [
-                ['42', str(id1), '1'],
-                ['', str(id2), '2'],
-            ]),
-            ok(2))
-
-        [b] = self.browse()
-        self.assertEqual(b.const, 42)
-        self.assertEqual(values(b.value), [1, 2])
-        self.assertEqual(values(b.value, field='parent_id'), [b, b])
-
-class test_o2m_multiple(ImporterCase):
-    model_name = 'export.one2many.multiple'
-
-    def test_multi_mixed(self):
-        self.assertEqual(
-            self.import_(['const', 'child1/value', 'child2/value'], [
-                ['5', '11', '21'],
-                ['', '12', '22'],
-                ['', '13', '23'],
-                ['', '14', ''],
-            ]),
-            ok(4))
-
-        [b] = self.browse()
-        self.assertEqual(values(b.child1), [11, 12, 13, 14])
-        self.assertEqual(values(b.child2), [21, 22, 23])
-
-    def test_multi(self):
-        self.assertEqual(
-            self.import_(['const', 'child1/value', 'child2/value'], [
-                ['5', '11', '21'],
-                ['', '12', ''],
-                ['', '13', ''],
-                ['', '14', ''],
-                ['', '', '22'],
-                ['', '', '23'],
-            ]),
-            ok(6))
-
-        [b] = self.browse()
-        self.assertEqual(values(b.child1), [11, 12, 13, 14])
-        self.assertEqual(values(b.child2), [21, 22, 23])
-
-    def test_multi_fullsplit(self):
-        self.assertEqual(
-            self.import_(['const', 'child1/value', 'child2/value'], [
-                ['5', '11', ''],
-                ['', '12', ''],
-                ['', '13', ''],
-                ['', '14', ''],
-                ['', '', '21'],
-                ['', '', '22'],
-                ['', '', '23'],
-            ]),
-            ok(7))
-
-        [b] = self.browse()
-        self.assertEqual(b.const, 5)
-        self.assertEqual(values(b.child1), [11, 12, 13, 14])
-        self.assertEqual(values(b.child2), [21, 22, 23])
-
-# function, related, reference: written to db as-is...
-# => function uses @type for value coercion/conversion
diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py
deleted file mode 100644 (file)
index 2604922..0000000
+++ /dev/null
@@ -1,1173 +0,0 @@
-# -*- coding: utf-8 -*-
-import json
-import pkgutil
-import unittest2
-
-import openerp.modules.registry
-import openerp
-
-from openerp.tests import common
-from openerp.tools.misc import mute_logger
-
-def message(msg, type='error', from_=0, to_=0, record=0, field='value', **kwargs):
-    return dict(kwargs,
-                type=type, rows={'from': from_, 'to': to_}, record=record,
-                field=field, message=msg)
-def moreaction(**kwargs):
-    return dict(kwargs,
-        type='ir.actions.act_window',
-        target='new',
-        view_mode='tree,form',
-        view_type='form',
-        views=[(False, 'tree'), (False, 'form')],
-        help=u"See all possible values")
-
-def values(seq, field='value'):
-    return [item[field] for item in seq]
-
-class ImporterCase(common.TransactionCase):
-    model_name = False
-
-    def __init__(self, *args, **kwargs):
-        super(ImporterCase, self).__init__(*args, **kwargs)
-        self.model = None
-
-    def setUp(self):
-        super(ImporterCase, self).setUp()
-        self.model = self.registry(self.model_name)
-        self.registry('ir.model.data').clear_caches()
-
-    def import_(self, fields, rows, context=None):
-        return self.model.load(
-            self.cr, openerp.SUPERUSER_ID, fields, rows, context=context)
-    def read(self, fields=('value',), domain=(), context=None):
-        return self.model.read(
-            self.cr, openerp.SUPERUSER_ID,
-            self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context),
-            fields=fields, context=context)
-    def browse(self, domain=(), context=None):
-        return self.model.browse(
-            self.cr, openerp.SUPERUSER_ID,
-            self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context),
-            context=context)
-
-    def xid(self, record):
-        ModelData = self.registry('ir.model.data')
-
-        ids = ModelData.search(
-            self.cr, openerp.SUPERUSER_ID,
-            [('model', '=', record._table_name), ('res_id', '=', record.id)])
-        if ids:
-            d = ModelData.read(
-                self.cr, openerp.SUPERUSER_ID, ids, ['name', 'module'])[0]
-            if d['module']:
-                return '%s.%s' % (d['module'], d['name'])
-            return d['name']
-
-        name = dict(record.name_get())[record.id]
-        # fix dotted name_get results, otherwise xid lookups blow up
-        name = name.replace('.', '-')
-        ModelData.create(self.cr, openerp.SUPERUSER_ID, {
-            'name': name,
-            'model': record._table_name,
-            'res_id': record.id,
-            'module': '__test__'
-        })
-        return '__test__.' + name
-
-    def add_translations(self, name, type, code, *tnx):
-        Lang = self.registry('res.lang')
-        if not Lang.search(self.cr, openerp.SUPERUSER_ID, [('code', '=', code)]):
-            Lang.create(self.cr, openerp.SUPERUSER_ID, {
-                'name': code,
-                'code': code,
-                'translatable': True,
-                'date_format': '%d.%m.%Y',
-                'decimal_point': ',',
-            })
-        Translations = self.registry('ir.translation')
-        for source, value in tnx:
-            Translations.create(self.cr, openerp.SUPERUSER_ID, {
-                'name': name,
-                'lang': code,
-                'type': type,
-                'src': source,
-                'value': value,
-                'state': 'translated',
-            })
-
-class test_ids_stuff(ImporterCase):
-    model_name = 'export.integer'
-
-    def test_create_with_id(self):
-        result = self.import_(['.id', 'value'], [['42', '36']])
-        self.assertIs(result['ids'], False)
-        self.assertEqual(result['messages'], [{
-            'type': 'error',
-            'rows': {'from': 0, 'to': 0},
-            'record': 0,
-            'field': '.id',
-            'message': u"Unknown database identifier '42'",
-        }])
-    def test_create_with_xid(self):
-        result = self.import_(['id', 'value'], [['somexmlid', '42']])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-        self.assertEqual(
-            'somexmlid',
-            self.xid(self.browse()[0]))
-
-    def test_update_with_id(self):
-        id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': 36})
-        self.assertEqual(
-            36,
-            self.model.browse(self.cr, openerp.SUPERUSER_ID, id).value)
-
-        result = self.import_(['.id', 'value'], [[str(id), '42']])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-        self.assertEqual(
-            [42], # updated value to imported
-            values(self.read()))
-
-    def test_update_with_xid(self):
-        self.import_(['id', 'value'], [['somexmlid', '36']])
-        self.assertEqual([36], values(self.read()))
-
-        self.import_(['id', 'value'], [['somexmlid', '1234567']])
-        self.assertEqual([1234567], values(self.read()))
-
-class test_boolean_field(ImporterCase):
-    model_name = 'export.boolean'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.import_(['value'], []),
-            {'ids': [], 'messages': []})
-
-    def test_exported(self):
-        result = self.import_(['value'], [['False'], ['True'], ])
-        self.assertEqual(len(result['ids']), 2)
-        self.assertFalse(result['messages'])
-        records = self.read()
-        self.assertEqual([
-            False,
-            True,
-        ], values(records))
-
-    def test_falses(self):
-        for lang, source, value in [('fr_FR', 'no', u'non'),
-                                    ('de_DE', 'no', u'nein'),
-                                    ('ru_RU', 'no', u'нет'),
-                                    ('nl_BE', 'false', u'vals'),
-                                    ('lt_LT', 'false', u'klaidingas')]:
-            self.add_translations('test_import.py', 'code', lang, (source, value))
-        falses = [[u'0'], [u'no'], [u'false'], [u'FALSE'], [u''],
-                  [u'non'], # no, fr
-                  [u'nein'], # no, de
-                  [u'нет'], # no, ru
-                  [u'vals'], # false, nl
-                  [u'klaidingas'], # false, lt,
-        ]
-
-        result = self.import_(['value'], falses)
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), len(falses))
-        self.assertEqual([False] * len(falses), values(self.read()))
-
-    def test_trues(self):
-        trues = [['None'], ['nil'], ['()'], ['f'], ['#f'],
-                  # Problem: OpenOffice (and probably excel) output localized booleans
-                  ['VRAI'], ['ok'], ['true'], ['yes'], ['1'], ]
-        result = self.import_(['value'], trues)
-        self.assertEqual(len(result['ids']), 10)
-        self.assertEqual(result['messages'], [
-            message(u"Unknown value '%s' for boolean field 'unknown', assuming 'yes'" % v[0],
-                    moreinfo=u"Use '1' for yes and '0' for no",
-                    type='warning', from_=i, to_=i, record=i)
-            for i, v in enumerate(trues)
-            if v[0] not in ('true', 'yes', '1')
-        ])
-        self.assertEqual(
-            [True] * 10,
-            values(self.read()))
-
-class test_integer_field(ImporterCase):
-    model_name = 'export.integer'
-
-    def test_none(self):
-        self.assertEqual(
-            self.import_(['value'], []),
-            {'ids': [], 'messages': []})
-
-    def test_empty(self):
-        result = self.import_(['value'], [['']])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-        self.assertEqual(
-            [False],
-            values(self.read()))
-
-    def test_zero(self):
-        result = self.import_(['value'], [['0']])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-
-        result = self.import_(['value'], [['-0']])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-
-        self.assertEqual([False, False], values(self.read()))
-
-    def test_positives(self):
-        result = self.import_(['value'], [
-            ['1'],
-            ['42'],
-            [str(2**31-1)],
-            ['12345678']
-        ])
-        self.assertEqual(len(result['ids']), 4)
-        self.assertFalse(result['messages'])
-
-        self.assertEqual([
-            1, 42, 2**31-1, 12345678
-        ], values(self.read()))
-
-    def test_negatives(self):
-        result = self.import_(['value'], [
-            ['-1'],
-            ['-42'],
-            [str(-(2**31 - 1))],
-            [str(-(2**31))],
-            ['-12345678']
-        ])
-        self.assertEqual(len(result['ids']), 5)
-        self.assertFalse(result['messages'])
-        self.assertEqual([
-            -1, -42, -(2**31 - 1), -(2**31), -12345678
-        ], values(self.read()))
-
-    @mute_logger('openerp.sql_db', 'openerp.osv.orm')
-    def test_out_of_range(self):
-        result = self.import_(['value'], [[str(2**31)]])
-        self.assertIs(result['ids'], False)
-        self.assertEqual(result['messages'], [{
-            'type': 'error',
-            'rows': {'from': 0, 'to': 0},
-            'record': 0,
-            'message': "integer out of range\n"
-        }])
-
-        result = self.import_(['value'], [[str(-2**32)]])
-        self.assertIs(result['ids'], False)
-        self.assertEqual(result['messages'], [{
-            'type': 'error',
-            'rows': {'from': 0, 'to': 0},
-            'record': 0,
-            'message': "integer out of range\n"
-        }])
-
-    def test_nonsense(self):
-        result = self.import_(['value'], [['zorglub']])
-        self.assertIs(result['ids'], False)
-        self.assertEqual(result['messages'], [{
-            'type': 'error',
-            'rows': {'from': 0, 'to': 0},
-            'record': 0,
-            'field': 'value',
-            'message': u"'zorglub' does not seem to be an integer for field 'unknown'",
-        }])
-
-class test_float_field(ImporterCase):
-    model_name = 'export.float'
-    def test_none(self):
-        self.assertEqual(
-            self.import_(['value'], []),
-            {'ids': [], 'messages': []})
-
-    def test_empty(self):
-        result = self.import_(['value'], [['']])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-        self.assertEqual(
-            [False],
-            values(self.read()))
-
-    def test_zero(self):
-        result = self.import_(['value'], [['0']])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-
-        result = self.import_(['value'], [['-0']])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-
-        self.assertEqual([False, False], values(self.read()))
-
-    def test_positives(self):
-        result = self.import_(['value'], [
-            ['1'],
-            ['42'],
-            [str(2**31-1)],
-            ['12345678'],
-            [str(2**33)],
-            ['0.000001'],
-        ])
-        self.assertEqual(len(result['ids']), 6)
-        self.assertFalse(result['messages'])
-
-        self.assertEqual([
-            1, 42, 2**31-1, 12345678, 2.0**33, .000001
-        ], values(self.read()))
-
-    def test_negatives(self):
-        result = self.import_(['value'], [
-            ['-1'],
-            ['-42'],
-            [str(-2**31 + 1)],
-            [str(-2**31)],
-            ['-12345678'],
-            [str(-2**33)],
-            ['-0.000001'],
-        ])
-        self.assertEqual(len(result['ids']), 7)
-        self.assertFalse(result['messages'])
-        self.assertEqual([
-            -1, -42, -(2**31 - 1), -(2**31), -12345678, -2.0**33, -.000001
-        ], values(self.read()))
-
-    def test_nonsense(self):
-        result = self.import_(['value'], [['foobar']])
-        self.assertIs(result['ids'], False)
-        self.assertEqual(result['messages'], [
-            message(u"'foobar' does not seem to be a number for field 'unknown'")])
-
-class test_string_field(ImporterCase):
-    model_name = 'export.string.bounded'
-
-    def test_empty(self):
-        result = self.import_(['value'], [['']])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-        self.assertEqual([False], values(self.read()))
-
-    def test_imported(self):
-        result = self.import_(['value'], [
-            [u'foobar'],
-            [u'foobarbaz'],
-            [u'Með suð í eyrum við spilum endalaust'],
-            [u"People 'get' types. They use them all the time. Telling "
-             u"someone he can't pound a nail with a banana doesn't much "
-             u"surprise him."]
-        ])
-        self.assertEqual(len(result['ids']), 4)
-        self.assertFalse(result['messages'])
-        self.assertEqual([
-            u"foobar",
-            u"foobarbaz",
-            u"Með suð í eyrum ",
-            u"People 'get' typ",
-        ], values(self.read()))
-
-class test_unbound_string_field(ImporterCase):
-    model_name = 'export.string'
-
-    def test_imported(self):
-        result = self.import_(['value'], [
-            [u'í dag viðrar vel til loftárása'],
-            # ackbar.jpg
-            [u"If they ask you about fun, you tell them – fun is a filthy"
-             u" parasite"]
-        ])
-        self.assertEqual(len(result['ids']), 2)
-        self.assertFalse(result['messages'])
-        self.assertEqual([
-            u"í dag viðrar vel til loftárása",
-            u"If they ask you about fun, you tell them – fun is a filthy parasite"
-        ], values(self.read()))
-
-class test_required_string_field(ImporterCase):
-    model_name = 'export.string.required'
-
-    @mute_logger('openerp.sql_db', 'openerp.osv.orm')
-    def test_empty(self):
-        result = self.import_(['value'], [[]])
-        self.assertEqual(result['messages'], [message(
-            u"Missing required value for the field 'unknown' (value)")])
-        self.assertIs(result['ids'], False)
-
-    @mute_logger('openerp.sql_db', 'openerp.osv.orm')
-    def test_not_provided(self):
-        result = self.import_(['const'], [['12']])
-        self.assertEqual(result['messages'], [message(
-            u"Missing required value for the field 'unknown' (value)")])
-        self.assertIs(result['ids'], False)
-
-class test_text(ImporterCase):
-    model_name = 'export.text'
-
-    def test_empty(self):
-        result = self.import_(['value'], [['']])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-        self.assertEqual([False], values(self.read()))
-
-    def test_imported(self):
-        s = (u"Breiðskífa er notað um útgefna hljómplötu sem inniheldur "
-             u"stúdíóupptökur frá einum flytjanda. Breiðskífur eru oftast "
-             u"milli 25-80 mínútur og er lengd þeirra oft miðuð við 33⅓ "
-             u"snúninga 12 tommu vínylplötur (sem geta verið allt að 30 mín "
-             u"hvor hlið).\n\nBreiðskífur eru stundum tvöfaldar og eru þær þá"
-             u" gefnar út á tveimur geisladiskum eða tveimur vínylplötum.")
-        result = self.import_(['value'], [[s]])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-        self.assertEqual([s], values(self.read()))
-
-class test_selection(ImporterCase):
-    model_name = 'export.selection'
-    translations_fr = [
-        ("Foo", "tete"),
-        ("Bar", "titi"),
-        ("Qux", "toto"),
-    ]
-
-    def test_imported(self):
-        result = self.import_(['value'], [
-            ['Qux'],
-            ['Bar'],
-            ['Foo'],
-            ['2'],
-        ])
-        self.assertEqual(len(result['ids']), 4)
-        self.assertFalse(result['messages'])
-        self.assertEqual([3, 2, 1, 2], values(self.read()))
-
-    def test_imported_translated(self):
-        self.add_translations(
-            'export.selection,value', 'selection', 'fr_FR', *self.translations_fr)
-
-        result = self.import_(['value'], [
-            ['toto'],
-            ['tete'],
-            ['titi'],
-        ], context={'lang': 'fr_FR'})
-        self.assertEqual(len(result['ids']), 3)
-        self.assertFalse(result['messages'])
-
-        self.assertEqual([3, 1, 2], values(self.read()))
-
-        result = self.import_(['value'], [['Foo']], context={'lang': 'fr_FR'})
-        self.assertEqual(len(result['ids']), 1)
-        self.assertFalse(result['messages'])
-
-    def test_invalid(self):
-        result = self.import_(['value'], [['Baz']])
-        self.assertIs(result['ids'], False)
-        self.assertEqual(result['messages'], [message(
-            u"Value 'Baz' not found in selection field 'unknown'",
-            moreinfo="Foo Bar Qux 4".split())])
-
-        result = self.import_(['value'], [[42]])
-        self.assertIs(result['ids'], False)
-        self.assertEqual(result['messages'], [message(
-            u"Value '42' not found in selection field 'unknown'",
-            moreinfo="Foo Bar Qux 4".split())])
-
-class test_selection_with_default(ImporterCase):
-    model_name = 'export.selection.withdefault'
-
-    def test_empty(self):
-        """ Empty cells should set corresponding field to False
-        """
-        result = self.import_(['value'], [['']])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        self.assertEqual(
-            values(self.read()),
-            [False])
-
-    def test_default(self):
-        """ Non-provided cells should set corresponding field to default
-        """
-        result = self.import_(['const'], [['42']])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        self.assertEqual(
-            values(self.read()),
-            [2])
-
-class test_selection_function(ImporterCase):
-    model_name = 'export.selection.function'
-    translations_fr = [
-        ("Corge", "toto"),
-        ("Grault", "titi"),
-        ("Wheee", "tete"),
-        ("Moog", "tutu"),
-    ]
-
-    def test_imported(self):
-        """ import uses fields_get, so translates import label (may or may not
-        be good news) *and* serializes the selection function to reverse it:
-        import does not actually know that the selection field uses a function
-        """
-        # NOTE: conflict between a value and a label => pick first
-        result = self.import_(['value'], [
-            ['3'],
-            ["Grault"],
-        ])
-        self.assertEqual(len(result['ids']), 2)
-        self.assertFalse(result['messages'])
-        self.assertEqual(
-            ['3', '1'],
-            values(self.read()))
-
-    def test_translated(self):
-        """ Expects output of selection function returns translated labels
-        """
-        self.add_translations(
-            'export.selection,value', 'selection', 'fr_FR', *self.translations_fr)
-
-        result = self.import_(['value'], [
-            ['titi'],
-            ['tete'],
-        ], context={'lang': 'fr_FR'})
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 2)
-        self.assertEqual(values(self.read()), ['1', '2'])
-
-        result = self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'})
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-class test_m2o(ImporterCase):
-    model_name = 'export.many2one'
-
-    def test_by_name(self):
-        # create integer objects
-        integer_id1 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        integer_id2 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 36})
-        # get its name
-        name1 = dict(self.registry('export.integer').name_get(
-            self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1]
-        name2 = dict(self.registry('export.integer').name_get(
-            self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2]
-
-        result = self.import_(['value'], [
-            # import by name_get
-            [name1],
-            [name1],
-            [name2],
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 3)
-        # correct ids assigned to corresponding records
-        self.assertEqual([
-            (integer_id1, name1),
-            (integer_id1, name1),
-            (integer_id2, name2),],
-            values(self.read()))
-
-    def test_by_xid(self):
-        ExportInteger = self.registry('export.integer')
-        integer_id = ExportInteger.create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        xid = self.xid(ExportInteger.browse(
-            self.cr, openerp.SUPERUSER_ID, [integer_id])[0])
-
-        result = self.import_(['value/id'], [[xid]])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-        b = self.browse()
-        self.assertEqual(42, b[0].value.value)
-
-    def test_by_id(self):
-        integer_id = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        result = self.import_(['value/.id'], [[integer_id]])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-        b = self.browse()
-        self.assertEqual(42, b[0].value.value)
-
-    def test_by_names(self):
-        integer_id1 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        integer_id2 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        name1 = dict(self.registry('export.integer').name_get(
-            self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1]
-        name2 = dict(self.registry('export.integer').name_get(
-            self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2]
-        # names should be the same
-        self.assertEqual(name1, name2)
-
-        result = self.import_(['value'], [[name2]])
-        self.assertEqual(
-            result['messages'],
-            [message(u"Found multiple matches for field 'unknown' (2 matches)",
-                     type='warning')])
-        self.assertEqual(len(result['ids']), 1)
-        self.assertEqual([
-            (integer_id1, name1)
-        ], values(self.read()))
-
-    def test_fail_by_implicit_id(self):
-        """ Can't implicitly import records by id
-        """
-        # create integer objects
-        integer_id1 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 42})
-        integer_id2 = self.registry('export.integer').create(
-            self.cr, openerp.SUPERUSER_ID, {'value': 36})
-
-        # Because name_search all the things. Fallback schmallback
-        result = self.import_(['value'], [
-                # import by id, without specifying it
-                [integer_id1],
-                [integer_id2],
-                [integer_id1],
-        ])
-        self.assertEqual(result['messages'], [
-            message(u"No matching record found for name '%s' in field 'unknown'" % id,
-                    from_=index, to_=index, record=index,
-                    moreinfo=moreaction(res_model='export.integer'))
-            for index, id in enumerate([integer_id1, integer_id2, integer_id1])])
-        self.assertIs(result['ids'], False)
-
-    @mute_logger('openerp.sql_db')
-    def test_fail_id_mistype(self):
-        result = self.import_(['value/.id'], [["foo"]])
-
-        self.assertEqual(result['messages'], [
-            message(u"Invalid database id 'foo' for the field 'unknown'",
-                    moreinfo=moreaction(res_model='ir.model.data',
-                                        domain=[('model','=','export.integer')]))
-        ])
-        self.assertIs(result['ids'], False)
-
-    def test_sub_field(self):
-        """ Does not implicitly create the record, does not warn that you can't
-        import m2o subfields (at all)...
-        """
-        result = self.import_(['value/value'], [['42']])
-        self.assertEqual(result['messages'], [
-            message(u"Can not create Many-To-One records indirectly, import "
-                    u"the field separately")])
-        self.assertIs(result['ids'], False)
-
-    def test_fail_noids(self):
-        result = self.import_(['value'], [['nameisnoexist:3']])
-        self.assertEqual(result['messages'], [message(
-            u"No matching record found for name 'nameisnoexist:3' "
-            u"in field 'unknown'", moreinfo=moreaction(
-                res_model='export.integer'))])
-        self.assertIs(result['ids'], False)
-
-        result = self.import_(['value/id'], [['noxidhere']])
-        self.assertEqual(result['messages'], [message(
-            u"No matching record found for external id 'noxidhere' "
-            u"in field 'unknown'", moreinfo=moreaction(
-                res_model='ir.model.data', domain=[('model','=','export.integer')]))])
-        self.assertIs(result['ids'], False)
-
-        result = self.import_(['value/.id'], [['66']])
-        self.assertEqual(result['messages'], [message(
-            u"No matching record found for database id '66' "
-            u"in field 'unknown'", moreinfo=moreaction(
-                res_model='ir.model.data', domain=[('model','=','export.integer')]))])
-        self.assertIs(result['ids'], False)
-
-    def test_fail_multiple(self):
-        result = self.import_(
-            ['value', 'value/id'],
-            [['somename', 'somexid']])
-        self.assertEqual(result['messages'], [message(
-            u"Ambiguous specification for field 'unknown', only provide one of "
-            u"name, external id or database id")])
-        self.assertIs(result['ids'], False)
-
-class test_m2m(ImporterCase):
-    model_name = 'export.many2many'
-
-    # apparently, one and only thing which works is a
-    # csv_internal_sep-separated list of ids, xids, or names (depending if
-    # m2m/.id, m2m/id or m2m[/anythingelse]
-    def test_ids(self):
-        id1 = self.registry('export.many2many.other').create(
-                self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
-        id2 = self.registry('export.many2many.other').create(
-                self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
-        id3 = self.registry('export.many2many.other').create(
-                self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
-        id4 = self.registry('export.many2many.other').create(
-                self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
-        id5 = self.registry('export.many2many.other').create(
-                self.cr, openerp.SUPERUSER_ID, {'value': 99, 'str': 'record4'})
-
-        result = self.import_(['value/.id'], [
-            ['%d,%d' % (id1, id2)],
-            ['%d,%d,%d' % (id1, id3, id4)],
-            ['%d,%d,%d' % (id1, id2, id3)],
-            ['%d' % id5]
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 4)
-
-        ids = lambda records: [record.id for record in records]
-
-        b = self.browse()
-        self.assertEqual(ids(b[0].value), [id1, id2])
-        self.assertEqual(values(b[0].value), [3, 44])
-
-        self.assertEqual(ids(b[2].value), [id1, id2, id3])
-        self.assertEqual(values(b[2].value), [3, 44, 84])
-
-    def test_noids(self):
-        result = self.import_(['value/.id'], [['42']])
-        self.assertEqual(result['messages'], [message(
-            u"No matching record found for database id '42' in field "
-            u"'unknown'", moreinfo=moreaction(
-                res_model='ir.model.data', domain=[('model','=','export.many2many.other')]))])
-        self.assertIs(result['ids'], False)
-
-    def test_xids(self):
-        M2O_o = self.registry('export.many2many.other')
-        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
-        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
-        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
-        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
-        records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4])
-
-        result = self.import_(['value/id'], [
-            ['%s,%s' % (self.xid(records[0]), self.xid(records[1]))],
-            ['%s' % self.xid(records[3])],
-            ['%s,%s' % (self.xid(records[2]), self.xid(records[1]))],
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 3)
-
-        b = self.browse()
-        self.assertEqual(values(b[0].value), [3, 44])
-        self.assertEqual(values(b[2].value), [44, 84])
-    def test_noxids(self):
-        result = self.import_(['value/id'], [['noxidforthat']])
-        self.assertEqual(result['messages'], [message(
-            u"No matching record found for external id 'noxidforthat' in field"
-            u" 'unknown'", moreinfo=moreaction(
-                res_model='ir.model.data', domain=[('model','=','export.many2many.other')]))])
-        self.assertIs(result['ids'], False)
-
-    def test_names(self):
-        M2O_o = self.registry('export.many2many.other')
-        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
-        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
-        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
-        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
-        records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4])
-
-        name = lambda record: dict(record.name_get())[record.id]
-
-        result = self.import_(['value'], [
-            ['%s,%s' % (name(records[1]), name(records[2]))],
-            ['%s,%s,%s' % (name(records[0]), name(records[1]), name(records[2]))],
-            ['%s,%s' % (name(records[0]), name(records[3]))],
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 3)
-
-        b = self.browse()
-        self.assertEqual(values(b[1].value), [3, 44, 84])
-        self.assertEqual(values(b[2].value), [3, 9])
-
-    def test_nonames(self):
-        result = self.import_(['value'], [['wherethem2mhavenonames']])
-        self.assertEqual(result['messages'], [message(
-            u"No matching record found for name 'wherethem2mhavenonames' in "
-            u"field 'unknown'", moreinfo=moreaction(
-                res_model='export.many2many.other'))])
-        self.assertIs(result['ids'], False)
-
-    def test_import_to_existing(self):
-        M2O_o = self.registry('export.many2many.other')
-        id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
-        id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
-        id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
-        id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
-
-        xid = 'myxid'
-        result = self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id1, id2)]])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-        result = self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id3, id4)]])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        b = self.browse()
-        self.assertEqual(len(b), 1)
-        # TODO: replacement of existing m2m values is correct?
-        self.assertEqual(values(b[0].value), [84, 9])
-
-class test_o2m(ImporterCase):
-    model_name = 'export.one2many'
-
-    def test_name_get(self):
-        s = u'Java is a DSL for taking large XML files and converting them ' \
-            u'to stack traces'
-        result = self.import_(
-            ['const', 'value'],
-            [['5', s]])
-        self.assertEqual(result['messages'], [message(
-            u"No matching record found for name '%s' in field 'unknown'" % s,
-            moreinfo=moreaction(res_model='export.one2many.child'))])
-        self.assertIs(result['ids'], False)
-
-    def test_single(self):
-        result = self.import_(['const', 'value/value'], [
-            ['5', '63']
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        (b,) = self.browse()
-        self.assertEqual(b.const, 5)
-        self.assertEqual(values(b.value), [63])
-
-    def test_multicore(self):
-        result = self.import_(['const', 'value/value'], [
-            ['5', '63'],
-            ['6', '64'],
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 2)
-
-        b1, b2 = self.browse()
-        self.assertEqual(b1.const, 5)
-        self.assertEqual(values(b1.value), [63])
-        self.assertEqual(b2.const, 6)
-        self.assertEqual(values(b2.value), [64])
-
-    def test_multisub(self):
-        result = self.import_(['const', 'value/value'], [
-            ['5', '63'],
-            ['', '64'],
-            ['', '65'],
-            ['', '66'],
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        (b,) = self.browse()
-        self.assertEqual(values(b.value), [63, 64, 65, 66])
-
-    def test_multi_subfields(self):
-        result = self.import_(['value/str', 'const', 'value/value'], [
-            ['this', '5', '63'],
-            ['is', '', '64'],
-            ['the', '', '65'],
-            ['rhythm', '', '66'],
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        (b,) = self.browse()
-        self.assertEqual(values(b.value), [63, 64, 65, 66])
-        self.assertEqual(
-            values(b.value, 'str'),
-            'this is the rhythm'.split())
-
-    def test_link_inline(self):
-        """ m2m-style specification for o2ms
-        """
-        id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Bf', 'value': 109
-        })
-        id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Me', 'value': 262
-        })
-
-        result = self.import_(['const', 'value/.id'], [
-            ['42', '%d,%d' % (id1, id2)]
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        [b] = self.browse()
-        self.assertEqual(b.const, 42)
-        # automatically forces link between core record and o2ms
-        self.assertEqual(values(b.value), [109, 262])
-        self.assertEqual(values(b.value, field='parent_id'), [b, b])
-
-    def test_link(self):
-        """ O2M relating to an existing record (update) force a LINK_TO as well
-        """
-        O2M = self.registry('export.one2many.child')
-        id1 = O2M.create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Bf', 'value': 109
-        })
-        id2 = O2M.create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Me', 'value': 262
-        })
-
-        result = self.import_(['const', 'value/.id'], [
-            ['42', str(id1)],
-            ['', str(id2)],
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        [b] = self.browse()
-        self.assertEqual(b.const, 42)
-        # automatically forces link between core record and o2ms
-        self.assertEqual(values(b.value), [109, 262])
-        self.assertEqual(values(b.value, field='parent_id'), [b, b])
-
-    def test_link_2(self):
-        O2M_c = self.registry('export.one2many.child')
-        id1 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Bf', 'value': 109
-        })
-        id2 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, {
-            'str': 'Me', 'value': 262
-        })
-
-        result = self.import_(['const', 'value/.id', 'value/value'], [
-            ['42', str(id1), '1'],
-            ['', str(id2), '2'],
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        [b] = self.browse()
-        self.assertEqual(b.const, 42)
-        self.assertEqual(values(b.value), [1, 2])
-        self.assertEqual(values(b.value, field='parent_id'), [b, b])
-
-class test_o2m_multiple(ImporterCase):
-    model_name = 'export.one2many.multiple'
-
-    def test_multi_mixed(self):
-        result = self.import_(['const', 'child1/value', 'child2/value'], [
-            ['5', '11', '21'],
-            ['', '12', '22'],
-            ['', '13', '23'],
-            ['', '14', ''],
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        [b] = self.browse()
-        self.assertEqual(values(b.child1), [11, 12, 13, 14])
-        self.assertEqual(values(b.child2), [21, 22, 23])
-
-    def test_multi(self):
-        result = self.import_(['const', 'child1/value', 'child2/value'], [
-            ['5', '11', '21'],
-            ['', '12', ''],
-            ['', '13', ''],
-            ['', '14', ''],
-            ['', '', '22'],
-            ['', '', '23'],
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        [b] = self.browse()
-        self.assertEqual(values(b.child1), [11, 12, 13, 14])
-        self.assertEqual(values(b.child2), [21, 22, 23])
-
-    def test_multi_fullsplit(self):
-        result = self.import_(['const', 'child1/value', 'child2/value'], [
-            ['5', '11', ''],
-            ['', '12', ''],
-            ['', '13', ''],
-            ['', '14', ''],
-            ['', '', '21'],
-            ['', '', '22'],
-            ['', '', '23'],
-        ])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-        [b] = self.browse()
-        self.assertEqual(b.const, 5)
-        self.assertEqual(values(b.child1), [11, 12, 13, 14])
-        self.assertEqual(values(b.child2), [21, 22, 23])
-
-class test_realworld(common.TransactionCase):
-    def test_bigfile(self):
-        data = json.loads(pkgutil.get_data(self.__module__, 'contacts_big.json'))
-        result = self.registry('res.partner').load(
-            self.cr, openerp.SUPERUSER_ID,
-            ['name', 'mobile', 'email', 'image'],
-            data)
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), len(data))
-
-    def test_backlink(self):
-        data = json.loads(pkgutil.get_data(self.__module__, 'contacts.json'))
-        result = self.registry('res.partner').load(
-            self.cr, openerp.SUPERUSER_ID,
-            ["name", "type", "street", "city", "country_id", "category_id",
-             "supplier", "customer", "is_company", "parent_id"],
-            data)
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), len(data))
-
-    def test_recursive_o2m(self):
-        """ The content of the o2m field's dict needs to go through conversion
-        as it may be composed of convertables or other relational fields
-        """
-        self.registry('ir.model.data').clear_caches()
-        Model = self.registry('export.one2many.recursive')
-        result = Model.load(self.cr, openerp.SUPERUSER_ID,
-            ['value', 'child/const', 'child/child1/str', 'child/child2/value'],
-            [
-                ['4', '42', 'foo', '55'],
-                ['', '43', 'bar', '56'],
-                ['', '', 'baz', ''],
-                ['', '55', 'qux', '57'],
-                ['5', '99', 'wheee', ''],
-                ['', '98', '', '12'],
-            ],
-        context=None)
-
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 2)
-
-        b = Model.browse(self.cr, openerp.SUPERUSER_ID, result['ids'], context=None)
-        self.assertEqual((b[0].value, b[1].value), (4, 5))
-
-        self.assertEqual([child.str for child in b[0].child[1].child1],
-                         ['bar', 'baz'])
-        self.assertFalse(len(b[1].child[1].child1))
-        self.assertEqual([child.value for child in b[1].child[1].child2],
-                         [12])
-
-class test_date(ImporterCase):
-    model_name = 'export.date'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.import_(['value'], []),
-            {'ids': [], 'messages': []})
-
-    def test_basic(self):
-        result = self.import_(['value'], [['2012-02-03']])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-    def test_invalid(self):
-        result = self.import_(['value'], [['not really a date']])
-        self.assertEqual(result['messages'], [
-            message(u"'not really a date' does not seem to be a valid date "
-                    u"for field 'unknown'",
-                    moreinfo=u"Use the format '2012-12-31'")])
-        self.assertIs(result['ids'], False)
-
-class test_datetime(ImporterCase):
-    model_name = 'export.datetime'
-
-    def test_empty(self):
-        self.assertEqual(
-            self.import_(['value'], []),
-            {'ids': [], 'messages': []})
-
-    def test_basic(self):
-        result = self.import_(['value'], [['2012-02-03 11:11:11']])
-        self.assertFalse(result['messages'])
-        self.assertEqual(len(result['ids']), 1)
-
-    def test_invalid(self):
-        result = self.import_(['value'], [['not really a datetime']])
-        self.assertEqual(result['messages'], [
-            message(u"'not really a datetime' does not seem to be a valid "
-                    u"datetime for field 'unknown'",
-                    moreinfo=u"Use the format '2012-12-31 23:59:59'")])
-        self.assertIs(result['ids'], False)
-
-    def test_checktz1(self):
-        """ Imported date should be interpreted as being in the tz provided by
-        the context
-        """
-        # write dummy tz in user (Asia/Hovd UTC+0700), should be superseded by
-        # context
-        self.registry('res.users').write(
-            self.cr, openerp.SUPERUSER_ID, [openerp.SUPERUSER_ID],
-            {'tz': 'Asia/Hovd'})
-
-        # UTC+1400
-        result = self.import_(
-            ['value'], [['2012-02-03 11:11:11']], {'tz': 'Pacific/Kiritimati'})
-        self.assertFalse(result['messages'])
-        self.assertEqual(
-            values(self.read(domain=[('id', 'in', result['ids'])])),
-            ['2012-02-02 21:11:11'])
-
-        # UTC-0930
-        result = self.import_(
-            ['value'], [['2012-02-03 11:11:11']], {'tz': 'Pacific/Marquesas'})
-        self.assertFalse(result['messages'])
-        self.assertEqual(
-            values(self.read(domain=[('id', 'in', result['ids'])])),
-            ['2012-02-03 20:41:11'])
-
-    def test_usertz(self):
-        """ If the context does not hold a timezone, the importing user's tz
-        should be used
-        """
-        # UTC +1000
-        self.registry('res.users').write(
-            self.cr, openerp.SUPERUSER_ID, [openerp.SUPERUSER_ID],
-            {'tz': 'Asia/Yakutsk'})
-
-        result = self.import_(
-            ['value'], [['2012-02-03 11:11:11']])
-        self.assertFalse(result['messages'])
-        self.assertEqual(
-            values(self.read(domain=[('id', 'in', result['ids'])])),
-            ['2012-02-03 01:11:11'])
-
-    def test_notz(self):
-        """ If there is no tz either in the context or on the user, falls back
-        to UTC
-        """
-        self.registry('res.users').write(
-            self.cr, openerp.SUPERUSER_ID, [openerp.SUPERUSER_ID],
-            {'tz': False})
-
-        result = self.import_(['value'], [['2012-02-03 11:11:11']])
-        self.assertFalse(result['messages'])
-        self.assertEqual(
-            values(self.read(domain=[('id', 'in', result['ids'])])),
-            ['2012-02-03 11:11:11'])
-
-class test_unique(ImporterCase):
-    model_name = 'export.unique'
-
-    @mute_logger('openerp.sql_db')
-    def test_unique(self):
-        result = self.import_(['value'], [
-            ['1'],
-            ['1'],
-            ['2'],
-            ['3'],
-            ['3'],
-        ])
-        self.assertFalse(result['ids'])
-        self.assertEqual(result['messages'], [
-            dict(message=u"The value for the field 'value' already exists. "
-                         u"This might be 'unknown' in the current model, "
-                         u"or a field of the same name in an o2m.",
-                 type='error', rows={'from': 1, 'to': 1},
-                 record=1, field='value'),
-            dict(message=u"The value for the field 'value' already exists. "
-                         u"This might be 'unknown' in the current model, "
-                         u"or a field of the same name in an o2m.",
-                 type='error', rows={'from': 4, 'to': 4},
-                 record=4, field='value'),
-        ])
diff --git a/openerp/tests/addons/test_limits/__init__.py b/openerp/tests/addons/test_limits/__init__.py
deleted file mode 100644 (file)
index fe44871..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# -*- coding: utf-8 -*-
-import models
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_limits/__openerp__.py b/openerp/tests/addons/test_limits/__openerp__.py
deleted file mode 100644 (file)
index 59972e6..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-{
-    'name': 'test-limits',
-    'version': '0.1',
-    'category': 'Tests',
-    'description': """A module with dummy methods.""",
-    'author': 'OpenERP SA',
-    'maintainer': 'OpenERP SA',
-    'website': 'http://www.openerp.com',
-    'depends': ['base'],
-    'data': ['ir.model.access.csv'],
-    'installable': True,
-    'auto_install': False,
-}
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_limits/ir.model.access.csv b/openerp/tests/addons/test_limits/ir.model.access.csv
deleted file mode 100644 (file)
index 02f1863..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
-access_test_limits_model,access_test_limits_model,model_test_limits_model,,1,1,1,1
diff --git a/openerp/tests/addons/test_limits/models.py b/openerp/tests/addons/test_limits/models.py
deleted file mode 100644 (file)
index 32e2a9a..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- coding: utf-8 -*-
-import time
-
-import openerp
-
-class m(openerp.osv.osv.Model):
-    """ This model exposes a few methods that will consume between 'almost no
-        resource' and 'a lot of resource'.
-    """
-    _name = 'test.limits.model'
-
-    def consume_nothing(self, cr, uid, context=None):
-        return True
-
-    def consume_memory(self, cr, uid, size, context=None):
-        l = [0] * size
-        return True
-
-    def leak_memory(self, cr, uid, size, context=None):
-        if not hasattr(self, 'l'):
-            self.l = []
-        self.l.append([0] * size)
-        return True
-
-    def consume_time(self, cr, uid, seconds, context=None):
-        time.sleep(seconds)
-        return True
-
-    def consume_cpu_time(self, cr, uid, seconds, context=None):
-        t0 = time.clock()
-        t1 = time.clock()
-        while t1 - t0 < seconds:
-            for i in xrange(10000000):
-                x = i * i
-            t1 = time.clock()
-        return True
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_uninstall/__init__.py b/openerp/tests/addons/test_uninstall/__init__.py
deleted file mode 100644 (file)
index fe44871..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# -*- coding: utf-8 -*-
-import models
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_uninstall/__openerp__.py b/openerp/tests/addons/test_uninstall/__openerp__.py
deleted file mode 100644 (file)
index 0015d5a..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-{
-    'name': 'test-uninstall',
-    'version': '0.1',
-    'category': 'Tests',
-    'description': """A module to test the uninstall feature.""",
-    'author': 'OpenERP SA',
-    'maintainer': 'OpenERP SA',
-    'website': 'http://www.openerp.com',
-    'depends': ['base'],
-    'data': ['ir.model.access.csv'],
-    'installable': True,
-    'auto_install': False,
-}
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_uninstall/ir.model.access.csv b/openerp/tests/addons/test_uninstall/ir.model.access.csv
deleted file mode 100644 (file)
index f5b5b1f..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
-access_test_uninstall_model,access_test_uninstall_model,model_test_uninstall_model,,1,1,1,1
diff --git a/openerp/tests/addons/test_uninstall/models.py b/openerp/tests/addons/test_uninstall/models.py
deleted file mode 100644 (file)
index 96a5800..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-import openerp
-from openerp.osv import fields
-from openerp.osv.orm import Model
-
-class test_uninstall_model(Model):
-    """
-    This model uses different types of columns to make it possible to test
-    the uninstall feature of OpenERP.
-    """
-    _name = 'test_uninstall.model'
-
-    _columns = {
-        'name': fields.char('Name', size=64),
-        'ref': fields.many2one('res.users', string='User'),
-        'rel': fields.many2many('res.users', string='Users'),
-    }
-
-    _sql_constraints = [
-        ('name_uniq', 'unique (name)', 'Each name must be unique.')
-    ]
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_workflow/__init__.py b/openerp/tests/addons/test_workflow/__init__.py
deleted file mode 100644 (file)
index fe44871..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# -*- coding: utf-8 -*-
-import models
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_workflow/__openerp__.py b/openerp/tests/addons/test_workflow/__openerp__.py
deleted file mode 100644 (file)
index 12cafa2..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-{
-    'name': 'test-workflow',
-    'version': '0.1',
-    'category': 'Tests',
-    'description': """A module to play with workflows.""",
-    'author': 'OpenERP SA',
-    'maintainer': 'OpenERP SA',
-    'website': 'http://www.openerp.com',
-    'depends': ['base'],
-    'data': ['data.xml', 'ir.model.access.csv'],
-    'installable': True,
-    'auto_install': False,
-}
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_workflow/data.xml b/openerp/tests/addons/test_workflow/data.xml
deleted file mode 100644 (file)
index 6299baa..0000000
+++ /dev/null
@@ -1,517 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-    <data>
-
-        <record id="view_test_workflow_model" model="ir.ui.view">
-            <field name="name">Test workflow</field>
-            <field name="model">test.workflow.model</field>
-            <field name="arch" type="xml">
-                <form string="Test workflow">
-                          <button name="a-b" string="a-b" type="workflow" icon="gtk-ok" colspan="1"/>
-                          <label string="a-b"/>
-                          <button name="trigger" string="trigger" type="object" icon="gtk-ok" colspan="1"/>
-                          <label string="trigger"/>
-                </form>
-           </field>
-        </record>
-
-        <record id="action_test_workflow" model="ir.actions.act_window">
-            <field name="name">Test workflow</field>
-            <field name="type">ir.actions.act_window</field>
-            <field name="res_model">test.workflow.model</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">tree,form</field>
-        </record>
-
-        <menuitem icon="STOCK_PREFERENCES" id="base.menu_tests" name="Tests" sequence="1000000"/>
-
-        <menuitem id="menu_test_workflow" parent="base.menu_tests" name="Test workflow"/>
-
-        <menuitem id="menu_test_workflow_leaf"
-            name="Test workflow"
-            action="action_test_workflow"
-            parent="menu_test_workflow"/>
-
-
-        <record id="test_workflow_trigger_1" model="test.workflow.trigger">
-        <!-- A single trigger record, with known ID 1 -->
-        </record>
-
-        <!-- A simple workflow:
-
-            a -signal-> b -trigger-> c
-
-        -->
-        <record id="test_workflow" model="workflow">
-            <field name="name">test.workflow</field>
-            <field name="osv">test.workflow.model</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow"/>
-            <field name="flow_start">True</field>
-            <field name="name">a</field>
-            <field name="kind">function</field>
-            <field name="action">print_a()</field>
-        </record>
-        <record id="activity_b" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow"/>
-            <field name="name">b</field>
-            <field name="kind">function</field>
-            <field name="action">print_b()</field>
-        </record>
-        <record id="activity_c" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow"/>
-            <field name="flow_stop">True</field>
-            <field name="name">c</field>
-            <field name="kind">function</field>
-            <field name="action">print_c()</field>
-        </record>
-
-        <record id="trans_a_b" model="workflow.transition">
-            <field name="act_from" ref="activity_a"/>
-            <field name="act_to" ref="activity_b"/>
-            <field name="signal">a-b</field>
-        </record>
-        <record id="trans_b_c" model="workflow.transition">
-            <field name="act_from" ref="activity_b"/>
-            <field name="act_to" ref="activity_c"/>
-            <field name="condition">condition()</field>
-            <field name="trigger_model">test.workflow.trigger</field>
-            <field name="trigger_expr_id">[1]</field>
-        </record>
-
-        <!-- Workflow A (a single activity):
-
-            a
-
-        -->
-        <record id="test_workflow_a" model="workflow">
-            <field name="name">test.workflow.a</field>
-            <field name="osv">test.workflow.model.a</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_a_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_a"/>
-            <field name="flow_start">True</field>
-            <field name="flow_stop">True</field>
-            <field name="name">a</field>
-            <field name="kind">dummy</field>
-        </record>
-
-        <!-- Workflow B (a single activity):
-
-            a
-
-        The function is run when the record is created.
-
-        -->
-        <record id="test_workflow_b" model="workflow">
-            <field name="name">test.workflow.b</field>
-            <field name="osv">test.workflow.model.b</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_b_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_b"/>
-            <field name="flow_start">True</field>
-            <field name="flow_stop">True</field>
-            <field name="name">a</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 1})</field>
-        </record>
-
-        <!-- Workflow C (a single activity):
-
-            a
-
-        The function is not run when the kind is dummy and no action_id is provided.
-        -->
-        <record id="test_workflow_c" model="workflow">
-            <field name="name">test.workflow.c</field>
-            <field name="osv">test.workflow.model.c</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_c_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_c"/>
-            <field name="flow_start">True</field>
-            <field name="flow_stop">True</field>
-            <field name="name">a</field>
-            <field name="kind">dummy</field>
-            <field name="action">write({'value': 1})</field>
-        </record>
-
-        <!-- Workflow D (a single activity):
-
-            a
-
-        The function is run when the kind is stopall and no action_id is provided.
-        -->
-        <record id="test_workflow_d" model="workflow">
-            <field name="name">test.workflow.d</field>
-            <field name="osv">test.workflow.model.d</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_d_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_d"/>
-            <field name="flow_start">True</field>
-            <field name="flow_stop">True</field>
-            <field name="name">a</field>
-            <field name="kind">stopall</field>
-            <field name="action">write({'value': 1})</field>
-        </record>
-
-        <!-- Workflow E:
-
-            a -True-> b
-
-        Both activities are run when the workflow is instanciated.
-        -->
-        <record id="test_workflow_e" model="workflow">
-            <field name="name">test.workflow.e</field>
-            <field name="osv">test.workflow.model.e</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_e_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_e"/>
-            <field name="flow_start">True</field>
-            <field name="name">a</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 1})</field>
-        </record>
-        <record id="activity_e_b" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_e"/>
-            <field name="flow_stop">True</field>
-            <field name="name">b</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 2})</field>
-        </record>
-
-        <record id="trans_e_a_b" model="workflow.transition">
-            <field name="act_from" ref="activity_e_a"/>
-            <field name="act_to" ref="activity_e_b"/>
-        </record>
-
-        <!-- Workflow F:
-
-            a -signal-> b
-
-        Same as E but with a signal on the transition.
-        -->
-        <record id="test_workflow_f" model="workflow">
-            <field name="name">test.workflow.f</field>
-            <field name="osv">test.workflow.model.f</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_f_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_f"/>
-            <field name="flow_start">True</field>
-            <field name="name">a</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 1})</field>
-        </record>
-        <record id="activity_f_b" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_f"/>
-            <field name="flow_stop">True</field>
-            <field name="name">b</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 2})</field>
-        </record>
-
-        <record id="trans_f_a_b" model="workflow.transition">
-            <field name="act_from" ref="activity_f_a"/>
-            <field name="act_to" ref="activity_f_b"/>
-            <field name="signal">a-b</field>
-        </record>
-
-        <!-- Workflow G:
-
-            a -False-> b
-
-        -->
-        <record id="test_workflow_g" model="workflow">
-            <field name="name">test.workflow.g</field>
-            <field name="osv">test.workflow.model.g</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_g_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_g"/>
-            <field name="flow_start">True</field>
-            <field name="name">a</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 1})</field>
-        </record>
-        <record id="activity_g_b" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_g"/>
-            <field name="flow_stop">True</field>
-            <field name="name">b</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 2})</field>
-        </record>
-
-        <record id="trans_g_a_b" model="workflow.transition">
-            <field name="act_from" ref="activity_g_a"/>
-            <field name="act_to" ref="activity_g_b"/>
-            <field name="condition">False</field>
-        </record>
-
-        <!-- Workflow H:
-
-            a or  -> b { value: 2 } 
-                 `-> c { value: 2 }
-
-        Whether the action of b or c is exectued last is non-deterministic.
-
-        -->
-        <record id="test_workflow_h" model="workflow">
-            <field name="name">test.workflow.h</field>
-            <field name="osv">test.workflow.model.h</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_h_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_h"/>
-            <field name="flow_start">True</field>
-            <field name="name">a</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 1})</field>
-            <field name="split_mode">OR</field>
-        </record>
-        <record id="activity_h_b" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_h"/>
-            <field name="flow_stop">True</field>
-            <field name="name">b</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 2})</field>
-        </record>
-        <record id="activity_h_c" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_h"/>
-            <field name="flow_stop">True</field>
-            <field name="name">c</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 2})</field>
-        </record>
-
-        <record id="trans_h_a_b" model="workflow.transition">
-            <field name="act_from" ref="activity_h_a"/>
-            <field name="act_to" ref="activity_h_b"/>
-        </record>
-        <record id="trans_h_a_c" model="workflow.transition">
-            <field name="act_from" ref="activity_h_a"/>
-            <field name="act_to" ref="activity_h_c"/>
-        </record>
-
-        <!-- Workflow I:
-
-            a or -> b { value: 2 } 
-                 `false> c { value: 3 }
-
-        -->
-        <record id="test_workflow_i" model="workflow">
-            <field name="name">test.workflow.i</field>
-            <field name="osv">test.workflow.model.i</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_i_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_i"/>
-            <field name="flow_start">True</field>
-            <field name="name">a</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 1})</field>
-            <field name="split_mode">OR</field>
-        </record>
-        <record id="activity_i_b" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_i"/>
-            <field name="flow_stop">True</field>
-            <field name="name">b</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 2})</field>
-        </record>
-        <record id="activity_i_c" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_i"/>
-            <field name="flow_stop">True</field>
-            <field name="name">c</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 3})</field>
-        </record>
-
-        <record id="trans_i_a_b" model="workflow.transition">
-            <field name="act_from" ref="activity_i_a"/>
-            <field name="act_to" ref="activity_i_b"/>
-        </record>
-        <record id="trans_i_a_c" model="workflow.transition">
-            <field name="act_from" ref="activity_i_a"/>
-            <field name="act_to" ref="activity_i_c"/>
-            <field name="condition">False</field>
-        </record>
-
-        <!-- Workflow J:
-
-            a and -> b { value: 2 } 
-                  `False> c { value: 3 }
-
-        This will stay in a because all transitions should be True at the same time.
-
-        -->
-        <record id="test_workflow_j" model="workflow">
-            <field name="name">test.workflow.j</field>
-            <field name="osv">test.workflow.model.j</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_j_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_j"/>
-            <field name="flow_start">True</field>
-            <field name="name">a</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 1})</field>
-            <field name="split_mode">AND</field>
-        </record>
-        <record id="activity_j_b" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_j"/>
-            <field name="flow_stop">True</field>
-            <field name="name">b</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 2})</field>
-        </record>
-        <record id="activity_j_c" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_j"/>
-            <field name="flow_stop">True</field>
-            <field name="name">c</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 3})</field>
-        </record>
-
-        <record id="trans_j_a_b" model="workflow.transition">
-            <field name="act_from" ref="activity_j_a"/>
-            <field name="act_to" ref="activity_j_b"/>
-        </record>
-        <record id="trans_j_a_c" model="workflow.transition">
-            <field name="act_from" ref="activity_j_a"/>
-            <field name="act_to" ref="activity_j_c"/>
-            <field name="condition">False</field>
-        </record>
-
-        <!-- Workflow K:
-
-            a xor -> b { value: 2 } 
-                  `> c { value: 2 }
-
-        Only one (truish) transition is taken with a XOR.
-
-        -->
-        <record id="test_workflow_k" model="workflow">
-            <field name="name">test.workflow.k</field>
-            <field name="osv">test.workflow.model.k</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_k_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_k"/>
-            <field name="flow_start">True</field>
-            <field name="name">a</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 1})</field>
-            <field name="split_mode">XOR</field>
-        </record>
-        <record id="activity_k_b" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_k"/>
-            <field name="flow_stop">True</field>
-            <field name="name">b</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 2})</field>
-        </record>
-        <record id="activity_k_c" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_k"/>
-            <field name="flow_stop">True</field>
-            <field name="name">c</field>
-            <field name="kind">function</field>
-           <field name="action">write({'value': 2})</field>
-        </record>
-
-        <record id="trans_k_a_b" model="workflow.transition">
-            <field name="act_from" ref="activity_k_a"/>
-            <field name="act_to" ref="activity_k_b"/>
-        </record>
-        <record id="trans_k_a_c" model="workflow.transition">
-            <field name="act_from" ref="activity_k_a"/>
-            <field name="act_to" ref="activity_k_c"/>
-        </record>
-
-        <!-- Workflow L:
-
-            a -> xor c { value: 3 }
-            b ´
-            a -> and d { value: 3 }
-            b ´
-
-        c is run for each incoming (and taken) transition.
-        d is run once when all its incoming transitions are taken at the same time.
-
-        -->
-        <record id="test_workflow_l" model="workflow">
-            <field name="name">test.workflow.l</field>
-            <field name="osv">test.workflow.model.l</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <record id="activity_l_a" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_l"/>
-            <field name="flow_start">True</field>
-            <field name="name">a</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 1})</field>
-            <field name="split_mode">OR</field>
-        </record>
-        <record id="activity_l_b" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_l"/>
-            <field name="flow_start">True</field>
-            <field name="name">b</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 2})</field>
-            <field name="split_mode">OR</field>
-        </record>
-        <record id="activity_l_c" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_l"/>
-            <field name="flow_stop">True</field>
-            <field name="name">c</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 3})</field>
-            <field name="join_mode">XOR</field>
-        </record>
-        <record id="activity_l_d" model="workflow.activity">
-            <field name="wkf_id" ref="test_workflow_l"/>
-            <field name="flow_stop">True</field>
-            <field name="name">d</field>
-            <field name="kind">function</field>
-            <field name="action">write({'value': 3})</field>
-            <field name="join_mode">AND</field>
-        </record>
-
-        <record id="trans_l_a_c" model="workflow.transition">
-            <field name="act_from" ref="activity_l_a"/>
-            <field name="act_to" ref="activity_l_c"/>
-        </record>
-        <record id="trans_l_b_c" model="workflow.transition">
-            <field name="act_from" ref="activity_l_b"/>
-            <field name="act_to" ref="activity_l_c"/>
-        </record>
-
-        <record id="trans_l_a_d" model="workflow.transition">
-            <field name="act_from" ref="activity_l_a"/>
-            <field name="act_to" ref="activity_l_d"/>
-        </record>
-        <record id="trans_l_b_d" model="workflow.transition">
-            <field name="act_from" ref="activity_l_b"/>
-            <field name="act_to" ref="activity_l_d"/>
-        </record>
-    </data>
-</openerp>
diff --git a/openerp/tests/addons/test_workflow/ir.model.access.csv b/openerp/tests/addons/test_workflow/ir.model.access.csv
deleted file mode 100644 (file)
index 6fb5a7a..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_test_workflow_model,access_test_workflow_model,model_test_workflow_model,,1,1,1,1
-access_test_workflow_trigger,access_test_workflow_trigger,model_test_workflow_trigger,,1,1,1,1
-access_test_workflow_model_a,access_test_workflow_model_a,model_test_workflow_model_a,,1,1,1,1
-access_test_workflow_model_b,access_test_workflow_model_b,model_test_workflow_model_b,,1,1,1,1
-access_test_workflow_model_c,access_test_workflow_model_c,model_test_workflow_model_c,,1,1,1,1
-access_test_workflow_model_d,access_test_workflow_model_d,model_test_workflow_model_d,,1,1,1,1
-access_test_workflow_model_e,access_test_workflow_model_e,model_test_workflow_model_e,,1,1,1,1
-access_test_workflow_model_f,access_test_workflow_model_f,model_test_workflow_model_f,,1,1,1,1
-access_test_workflow_model_g,access_test_workflow_model_g,model_test_workflow_model_g,,1,1,1,1
-access_test_workflow_model_h,access_test_workflow_model_h,model_test_workflow_model_h,,1,1,1,1
-access_test_workflow_model_i,access_test_workflow_model_i,model_test_workflow_model_i,,1,1,1,1
-access_test_workflow_model_j,access_test_workflow_model_j,model_test_workflow_model_j,,1,1,1,1
-access_test_workflow_model_k,access_test_workflow_model_k,model_test_workflow_model_k,,1,1,1,1
-access_test_workflow_model_l,access_test_workflow_model_l,model_test_workflow_model_l,,1,1,1,1
diff --git a/openerp/tests/addons/test_workflow/models.py b/openerp/tests/addons/test_workflow/models.py
deleted file mode 100644 (file)
index f7a5ef2..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-import openerp
-
-class m(openerp.osv.orm.Model):
-    """ A model for which we will define a workflow (see data.xml). """
-    _name = 'test.workflow.model'
-
-    def print_(self, cr, uid, ids, s, context=None):
-        print '  Running activity `%s` for record %s' % (s, ids)
-        return True
-
-    def print_a(self, cr, uid, ids, context=None):
-        return self.print_(cr, uid, ids, 'a', context)
-
-    def print_b(self, cr, uid, ids, context=None):
-        return self.print_(cr, uid, ids, 'b', context)
-
-    def print_c(self, cr, uid, ids, context=None):
-        return self.print_(cr, uid, ids, 'c', context)
-
-    def condition(self, cr, uid, ids, context=None):
-        m = self.pool['test.workflow.trigger']
-        for r in m.browse(cr, uid, [1], context=context):
-            if not r.value:
-                return False
-        return True
-
-    def trigger(self, cr, uid, context=None):
-        return openerp.workflow.trg_trigger(uid, 'test.workflow.trigger', 1, cr)
-
-class n(openerp.osv.orm.Model):
-    """ A model used for the trigger feature. """
-    _name = 'test.workflow.trigger'
-    _columns = { 'value': openerp.osv.fields.boolean('Value') }
-    _defaults = { 'value': False }
-
-class a(openerp.osv.orm.Model):
-    _name = 'test.workflow.model.a'
-    _columns = { 'value': openerp.osv.fields.integer('Value') }
-    _defaults = { 'value': 0 }
-
-class b(openerp.osv.orm.Model):
-    _name = 'test.workflow.model.b'
-    _inherit = 'test.workflow.model.a'
-
-class c(openerp.osv.orm.Model):
-    _name = 'test.workflow.model.c'
-    _inherit = 'test.workflow.model.a'
-
-class d(openerp.osv.orm.Model):
-    _name = 'test.workflow.model.d'
-    _inherit = 'test.workflow.model.a'
-
-class e(openerp.osv.orm.Model):
-    _name = 'test.workflow.model.e'
-    _inherit = 'test.workflow.model.a'
-
-for name in 'bcdefghijkl':
-    type(
-        name,
-        (openerp.osv.orm.Model,),
-        {
-            '_name': 'test.workflow.model.%s' % name,
-            '_inherit': 'test.workflow.model.a',
-        })
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_workflow/tests/__init__.py b/openerp/tests/addons/test_workflow/tests/__init__.py
deleted file mode 100644 (file)
index a09d09f..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from . import test_workflow
-
-fast_suite = [
-]
-
-checks = [
-    test_workflow,
-]
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_workflow/tests/test_workflow.py b/openerp/tests/addons/test_workflow/tests/test_workflow.py
deleted file mode 100644 (file)
index e9ff7b9..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-# -*- coding: utf-8 -*-
-import openerp
-from openerp import SUPERUSER_ID
-from openerp.tests import common
-
-
-class test_workflows(common.TransactionCase):
-
-    def check_activities(self, model_name, i, names):
-        """ Check that the record i has workitems in the given activity names.
-        """
-        instance = self.registry('workflow.instance')
-        workitem = self.registry('workflow.workitem')
-
-        # Given the workflow instance associated to the record ...
-        instance_id = instance.search(
-            self.cr, SUPERUSER_ID,
-            [('res_type', '=', model_name), ('res_id', '=', i)])
-        self.assertTrue( instance_id, 'A workflow instance is expected.')
-
-        # ... get all its workitems ...
-        workitem_ids = workitem.search(
-            self.cr, SUPERUSER_ID,
-            [('inst_id', '=', instance_id[0])])
-        self.assertTrue(
-            workitem_ids,
-            'The workflow instance should have workitems.')
-
-        # ... and check the activity the are in against the provided names.
-        workitem_records = workitem.browse(
-            self.cr, SUPERUSER_ID, workitem_ids)
-        self.assertEqual(
-            sorted([item.act_id.name for item in workitem_records]),
-            sorted(names))
-
-    def check_value(self, model_name, i, value):
-        """ Check that the record i has the given value.
-        """
-        model = self.registry(model_name)
-        record = model.read(self.cr, SUPERUSER_ID, [i], ['value'])[0]
-        self.assertEqual(record['value'], value)
-
-    def test_workflow(self):
-        model = self.registry('test.workflow.model')
-        trigger = self.registry('test.workflow.trigger')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['a'])
-
-        # a -> b is just a signal.
-        model.signal_workflow(self.cr, SUPERUSER_ID, [i], 'a-b')
-        self.check_activities(model._name, i, ['b'])
-
-        # b -> c is a trigger (which is False),
-        # so we remain in the b activity.
-        model.trigger(self.cr, SUPERUSER_ID, [i])
-        self.check_activities(model._name, i, ['b'])
-
-        # b -> c is a trigger (which is set to True).
-        # so we go in c when the trigger is called.
-        trigger.write(self.cr, SUPERUSER_ID, [1], {'value': True})
-        model.trigger(self.cr, SUPERUSER_ID)
-        self.check_activities(model._name, i, ['c'])
-
-        self.assertEqual(
-            True,
-            True)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_a(self):
-        model = self.registry('test.workflow.model.a')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['a'])
-        self.check_value(model._name, i, 0)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_b(self):
-        model = self.registry('test.workflow.model.b')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['a'])
-        self.check_value(model._name, i, 1)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_c(self):
-        model = self.registry('test.workflow.model.c')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['a'])
-        self.check_value(model._name, i, 0)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_d(self):
-        model = self.registry('test.workflow.model.d')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['a'])
-        self.check_value(model._name, i, 1)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_e(self):
-        model = self.registry('test.workflow.model.e')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['b'])
-        self.check_value(model._name, i, 2)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_f(self):
-        model = self.registry('test.workflow.model.f')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['a'])
-        self.check_value(model._name, i, 1)
-
-        model.signal_workflow(self.cr, SUPERUSER_ID, [i], 'a-b')
-        self.check_activities(model._name, i, ['b'])
-        self.check_value(model._name, i, 2)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_g(self):
-        model = self.registry('test.workflow.model.g')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['a'])
-        self.check_value(model._name, i, 1)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_h(self):
-        model = self.registry('test.workflow.model.h')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['b', 'c'])
-        self.check_value(model._name, i, 2)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_i(self):
-        model = self.registry('test.workflow.model.i')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['b'])
-        self.check_value(model._name, i, 2)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_j(self):
-        model = self.registry('test.workflow.model.j')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['a'])
-        self.check_value(model._name, i, 1)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_k(self):
-        model = self.registry('test.workflow.model.k')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        # Non-determinisitic: can be b or c
-        # self.check_activities(model._name, i, ['b'])
-        # self.check_activities(model._name, i, ['c'])
-        self.check_value(model._name, i, 2)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
-
-    def test_workflow_l(self):
-        model = self.registry('test.workflow.model.l')
-
-        i = model.create(self.cr, SUPERUSER_ID, {})
-        self.check_activities(model._name, i, ['c', 'c', 'd'])
-        self.check_value(model._name, i, 3)
-
-        model.unlink(self.cr, SUPERUSER_ID, [i])
index df00e61..6d47bc9 100644 (file)
@@ -3,15 +3,24 @@
 The module :mod:`openerp.tests.common` provides a few helpers and classes to write
 tests.
 """
+import json
+import os
+import select
+import subprocess
 import threading
 import time
 import unittest2
+import uuid
 import xmlrpclib
+import logging
 
 import openerp
 
+_logger = logging.getLogger(__name__)
+
 # The openerp library is supposed already configured.
 ADDONS_PATH = openerp.tools.config['addons_path']
+HOST = '127.0.0.1'
 PORT = openerp.tools.config['xmlrpc_port']
 DB = openerp.tools.config['db_name']
 
@@ -22,26 +31,11 @@ DB = openerp.tools.config['db_name']
 if not DB and hasattr(threading.current_thread(), 'dbname'):
     DB = threading.current_thread().dbname
 
-HOST = '127.0.0.1'
-
 ADMIN_USER = 'admin'
 ADMIN_USER_ID = openerp.SUPERUSER_ID
 ADMIN_PASSWORD = 'admin'
 
-def start_openerp():
-    """
-    Start the OpenERP server similary to the openerp-server script.
-    """
-    openerp.service.start_services()
-
-    # Ugly way to ensure the server is listening.
-    time.sleep(2)
-
-def stop_openerp():
-    """
-    Shutdown the OpenERP server similarly to a single ctrl-c.
-    """
-    openerp.service.stop_services()
+HTTP_SESSION = {}
 
 class BaseCase(unittest2.TestCase):
     """
diff --git a/openerp/tests/test_acl.py b/openerp/tests/test_acl.py
deleted file mode 100644 (file)
index 798561c..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-import unittest2
-from lxml import etree
-
-import openerp
-from openerp.tools.misc import mute_logger
-
-import common
-
-# test group that demo user should not have
-GROUP_TECHNICAL_FEATURES = 'base.group_no_one'
-
-
-class TestACL(common.TransactionCase):
-
-    def setUp(self):
-        super(TestACL, self).setUp()
-        self.res_currency = self.registry('res.currency')
-        self.res_partner = self.registry('res.partner')
-        self.res_users = self.registry('res.users')
-        _, self.demo_uid = self.registry('ir.model.data').get_object_reference(self.cr, self.uid, 'base', 'user_demo')
-        self.tech_group = self.registry('ir.model.data').get_object(self.cr, self.uid,
-                                                                    *(GROUP_TECHNICAL_FEATURES.split('.')))
-
-    def test_field_visibility_restriction(self):
-        """Check that model-level ``groups`` parameter effectively restricts access to that
-           field for users who do not belong to one of the explicitly allowed groups"""
-        # Verify the test environment first
-        original_fields = self.res_currency.fields_get(self.cr, self.demo_uid, [])
-        form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form')
-        view_arch = etree.fromstring(form_view.get('arch'))
-        has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES)
-        self.assertFalse(has_tech_feat, "`demo` user should not belong to the restricted group before the test")
-        self.assertTrue('accuracy' in original_fields, "'accuracy' field must be properly visible before the test")
-        self.assertNotEquals(view_arch.xpath("//field[@name='accuracy']"), [],
-                             "Field 'accuracy' must be found in view definition before the test")
-
-        # Restrict access to the field and check it's gone
-        self.res_currency._columns['accuracy'].groups = GROUP_TECHNICAL_FEATURES
-        fields = self.res_currency.fields_get(self.cr, self.demo_uid, [])
-        form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form')
-        view_arch = etree.fromstring(form_view.get('arch'))
-        self.assertFalse('accuracy' in fields, "'accuracy' field should be gone")
-        self.assertEquals(view_arch.xpath("//field[@name='accuracy']"), [],
-                          "Field 'accuracy' must not be found in view definition")
-
-        # Make demo user a member of the restricted group and check that the field is back
-        self.tech_group.write({'users': [(4, self.demo_uid)]})
-        has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES)
-        fields = self.res_currency.fields_get(self.cr, self.demo_uid, [])
-        form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form')
-        view_arch = etree.fromstring(form_view.get('arch'))
-        #import pprint; pprint.pprint(fields); pprint.pprint(form_view)
-        self.assertTrue(has_tech_feat, "`demo` user should now belong to the restricted group")
-        self.assertTrue('accuracy' in fields, "'accuracy' field must be properly visible again")
-        self.assertNotEquals(view_arch.xpath("//field[@name='accuracy']"), [],
-                             "Field 'accuracy' must be found in view definition again")
-
-        #cleanup
-        self.tech_group.write({'users': [(3, self.demo_uid)]})
-        self.res_currency._columns['accuracy'].groups = False
-
-    @mute_logger('openerp.osv.orm')
-    def test_field_crud_restriction(self):
-        "Read/Write RPC access to restricted field should be forbidden"
-        # Verify the test environment first
-        has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES)
-        self.assertFalse(has_tech_feat, "`demo` user should not belong to the restricted group")
-        self.assert_(self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids']))
-        self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []}))
-
-        # Now restrict access to the field and check it's forbidden
-        self.res_partner._columns['bank_ids'].groups = GROUP_TECHNICAL_FEATURES
-        with self.assertRaises(openerp.osv.orm.except_orm):
-            self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids'])
-        with self.assertRaises(openerp.osv.orm.except_orm):
-            self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []})
-
-        # Add the restricted group, and check that it works again
-        self.tech_group.write({'users': [(4, self.demo_uid)]})
-        has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES)
-        self.assertTrue(has_tech_feat, "`demo` user should now belong to the restricted group")
-        self.assert_(self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids']))
-        self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []}))
-
-        #cleanup
-        self.tech_group.write({'users': [(3, self.demo_uid)]})
-        self.res_partner._columns['bank_ids'].groups = False
-
-    def test_fields_browse_restriction(self):
-        """Test access to records having restricted fields"""
-        self.res_partner._columns['email'].groups = GROUP_TECHNICAL_FEATURES
-        try:
-            P = self.res_partner
-            pid = P.search(self.cr, self.demo_uid, [], limit=1)[0]
-            part = P.browse(self.cr, self.demo_uid, pid)
-            # accessing fields must no raise exceptions...
-            part.name
-            # ... except if they are restricted
-            with self.assertRaises(openerp.osv.orm.except_orm) as cm:
-                with mute_logger('openerp.osv.orm'):
-                    part.email
-
-            self.assertEqual(cm.exception.args[0], 'Access Denied')
-        finally:
-            self.res_partner._columns['email'].groups = False
-
-if __name__ == '__main__':
-    unittest2.main()
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/test_basecase.py b/openerp/tests/test_basecase.py
deleted file mode 100644 (file)
index 376494b..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-# -*- coding: utf-8 -*-
-import unittest2
-
-import common
-
-class test_single_transaction_case(common.SingleTransactionCase):
-    """
-    Check the whole-class transaction behavior of SingleTransactionCase.
-    """
-
-    def test_00(self):
-        """Create a partner."""
-        cr, uid = self.cr, self.uid
-        self.registry('res.partner').create(cr, uid, {'name': 'test_per_class_teardown_partner'})
-        ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
-        self.assertEqual(1, len(ids), "Test partner not found.")
-
-    def test_01(self):
-        """Find the created partner."""
-        cr, uid = self.cr, self.uid
-        ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
-        self.assertEqual(1, len(ids), "Test partner not found.")
-
-    def test_20a(self):
-        """ Create a partner with a XML ID """
-        cr, uid = self.cr, self.uid
-        res_partner = self.registry('res.partner')
-        ir_model_data = self.registry('ir.model.data')
-        pid, _ = res_partner.name_create(cr, uid, 'Mr Blue')
-        ir_model_data.create(cr, uid, {'name': 'test_partner_blue',
-                                       'module': 'base',
-                                       'model': 'res.partner',
-                                       'res_id': pid})
-    def test_20b(self):
-        """ Resolve xml id with ref() and browse_ref() """
-        cr, uid = self.cr, self.uid
-        res_partner = self.registry('res.partner')
-        xid = 'base.test_partner_blue'
-        p_ref = self.ref(xid)
-        self.assertTrue(p_ref, "ref() should resolve xid to database ID")
-        partner = res_partner.browse(cr, uid, p_ref)
-        p_browse_ref = self.browse_ref(xid)
-        self.assertEqual(partner, p_browse_ref, "browse_ref() should resolve xid to browse records")
-    
-
-
-class test_transaction_case(common.TransactionCase):
-    """
-    Check the per-method transaction behavior of TransactionCase.
-    """
-
-    def test_00(self):
-        """Create a partner."""
-        cr, uid = self.cr, self.uid
-        ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
-        self.assertEqual(0, len(ids), "Test partner found.")
-        self.registry('res.partner').create(cr, uid, {'name': 'test_per_class_teardown_partner'})
-        ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
-        self.assertEqual(1, len(ids), "Test partner not found.")
-
-    def test_01(self):
-        """Don't find the created partner."""
-        cr, uid = self.cr, self.uid
-        ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
-        self.assertEqual(0, len(ids), "Test partner found.")
-
-
-    def test_20a(self):
-        """ Create a partner with a XML ID then resolve xml id with ref() and browse_ref() """
-        cr, uid = self.cr, self.uid
-        res_partner = self.registry('res.partner')
-        ir_model_data = self.registry('ir.model.data')
-        pid, _ = res_partner.name_create(cr, uid, 'Mr Yellow')
-        ir_model_data.create(cr, uid, {'name': 'test_partner_yellow',
-                                       'module': 'base',
-                                       'model': 'res.partner',
-                                       'res_id': pid})
-        xid = 'base.test_partner_yellow'
-        p_ref = self.ref(xid)
-        self.assertEquals(p_ref, pid, "ref() should resolve xid to database ID")
-        partner = res_partner.browse(cr, uid, pid)
-        p_browse_ref = self.browse_ref(xid)
-        self.assertEqual(partner, p_browse_ref, "browse_ref() should resolve xid to browse records")
-
-if __name__ == '__main__':
-    unittest2.main()
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/test_db_cursor.py b/openerp/tests/test_db_cursor.py
deleted file mode 100644 (file)
index dba1cf4..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import unittest2
-
-import openerp
-from openerp.tools.misc import mute_logger
-import common
-
-DB = common.DB
-ADMIN_USER_ID = common.ADMIN_USER_ID
-
-def registry():
-    return openerp.modules.registry.RegistryManager.get(DB)
-
-
-class test_cr_execute(unittest2.TestCase):
-    """ Try cr.execute with wrong parameters """
-
-    @mute_logger('openerp.sql_db')
-    def test_execute_bad_params(self):
-        """
-        Try to use iterable but non-list or int params in query parameters.
-        """
-        with registry().cursor(auto_commit=False) as cr:
-            with self.assertRaises(ValueError):
-                cr.execute("SELECT id FROM res_users WHERE login=%s", 'admin')
-            with self.assertRaises(ValueError):
-                cr.execute("SELECT id FROM res_users WHERE id=%s", 1)
-            with self.assertRaises(ValueError):
-                cr.execute("SELECT id FROM res_users WHERE id=%s", '1')
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/test_fields.py b/openerp/tests/test_fields.py
deleted file mode 100644 (file)
index 774bbf4..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-#
-# test cases for fields access, etc.
-#
-import common
-
-from openerp.osv import fields
-
-class TestRelatedField(common.TransactionCase):
-
-    def setUp(self):
-        super(TestRelatedField, self).setUp()
-        self.partner = self.registry('res.partner')
-        self.company = self.registry('res.company')
-
-    def test_0_related(self):
-        """ test an usual related field """
-        # add a related field test_related_company_id on res.partner
-        old_columns = self.partner._columns
-        self.partner._columns = dict(old_columns)
-        self.partner._columns.update({
-            'related_company_partner_id': fields.related('company_id', 'partner_id', type='many2one', obj='res.partner'),
-        })
-
-        # find a company with a non-null partner_id
-        ids = self.company.search(self.cr, self.uid, [('partner_id', '!=', False)], limit=1)
-        id = ids[0]
-
-        # find partners that satisfy [('partner_id.company_id', '=', id)]
-        company_ids = self.company.search(self.cr, self.uid, [('partner_id', '=', id)])
-        partner_ids1 = self.partner.search(self.cr, self.uid, [('company_id', 'in', company_ids)])
-        partner_ids2 = self.partner.search(self.cr, self.uid, [('related_company_partner_id', '=', id)])
-        self.assertEqual(partner_ids1, partner_ids2)
-
-        # restore res.partner fields
-        self.partner._columns = old_columns
-
-    def do_test_company_field(self, field):
-        # get a partner with a non-null company_id
-        ids = self.partner.search(self.cr, self.uid, [('company_id', '!=', False)], limit=1)
-        partner = self.partner.browse(self.cr, self.uid, ids[0])
-
-        # check reading related field
-        self.assertEqual(partner[field], partner.company_id)
-
-        # check that search on related field is equivalent to original field
-        ids1 = self.partner.search(self.cr, self.uid, [('company_id', '=', partner.company_id.id)])
-        ids2 = self.partner.search(self.cr, self.uid, [(field, '=', partner.company_id.id)])
-        self.assertEqual(ids1, ids2)
-
-    def test_1_single_related(self):
-        """ test a related field with a single indirection like fields.related('foo') """
-        # add a related field test_related_company_id on res.partner
-        # and simulate a _inherits_reload() to populate _all_columns.
-        old_columns = self.partner._columns
-        old_all_columns = self.partner._all_columns
-        self.partner._columns = dict(old_columns)
-        self.partner._all_columns = dict(old_all_columns)
-        self.partner._columns.update({
-            'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'),
-        })
-        self.partner._all_columns.update({
-            'single_related_company_id': fields.column_info('single_related_company_id', self.partner._columns['single_related_company_id'], None, None, None)
-        })
-
-        self.do_test_company_field('single_related_company_id')
-
-        # restore res.partner fields
-        self.partner._columns = old_columns
-        self.partner._all_columns = old_all_columns
-
-    def test_2_related_related(self):
-        """ test a related field referring to a related field """
-        # add a related field on a related field on res.partner
-        # and simulate a _inherits_reload() to populate _all_columns.
-        old_columns = self.partner._columns
-        old_all_columns = self.partner._all_columns
-        self.partner._columns = dict(old_columns)
-        self.partner._all_columns = dict(old_all_columns)
-        self.partner._columns.update({
-            'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'),
-            'related_related_company_id': fields.related('single_related_company_id', type='many2one', obj='res.company'),
-        })
-        self.partner._all_columns.update({
-            'single_related_company_id': fields.column_info('single_related_company_id', self.partner._columns['single_related_company_id'], None, None, None),
-            'related_related_company_id': fields.column_info('related_related_company_id', self.partner._columns['related_related_company_id'], None, None, None)
-        })
-
-        self.do_test_company_field('related_related_company_id')
-
-        # restore res.partner fields
-        self.partner._columns = old_columns
-        self.partner._all_columns = old_all_columns
-
-    def test_3_read_write(self):
-        """ write on a related field """
-        # add a related field test_related_company_id on res.partner
-        old_columns = self.partner._columns
-        self.partner._columns = dict(old_columns)
-        self.partner._columns.update({
-            'related_company_partner_id': fields.related('company_id', 'partner_id', type='many2one', obj='res.partner'),
-        })
-
-        # find a company with a non-null partner_id
-        company_ids = self.company.search(self.cr, self.uid, [('partner_id', '!=', False)], limit=1)
-        company = self.company.browse(self.cr, self.uid, company_ids[0])
-
-        # find partners that satisfy [('partner_id.company_id', '=', company.id)]
-        partner_ids = self.partner.search(self.cr, self.uid, [('related_company_partner_id', '=', company.id)])
-        partner = self.partner.browse(self.cr, self.uid, partner_ids[0])
-
-        # create a new partner, and assign it to company
-        new_partner_id = self.partner.create(self.cr, self.uid, {'name': 'Foo'})
-        partner.write({'related_company_partner_id': new_partner_id})
-
-        company = self.company.browse(self.cr, self.uid, company_ids[0])
-        self.assertEqual(company.partner_id.id, new_partner_id)
-
-        partner = self.partner.browse(self.cr, self.uid, partner_ids[0])
-        self.assertEqual(partner.related_company_partner_id.id, new_partner_id)
-
-        # restore res.partner fields
-        self.partner._columns = old_columns
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/test_func.py b/openerp/tests/test_func.py
deleted file mode 100644 (file)
index 7bb2e8f..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-import functools
-import unittest2
-
-from ..tools.func import compose
-
-class TestCompose(unittest2.TestCase):
-    def test_basic(self):
-        str_add = compose(str, lambda a, b: a + b)
-        self.assertEqual(
-            str_add(1, 2),
-            "3")
-
-    def test_decorator(self):
-        """ ensure compose() can be partially applied as a decorator
-        """
-        @functools.partial(compose, unicode)
-        def mul(a, b):
-            return a * b
-
-        self.assertEqual(mul(5, 42), u"210")
-
diff --git a/openerp/tests/test_ir_filters.py b/openerp/tests/test_ir_filters.py
deleted file mode 100644 (file)
index 64d5c98..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-# -*- coding: utf-8 -*-
-import functools
-
-from openerp import exceptions
-from . import common
-
-def noid(d):
-    """ Removes `id` key from a dict so we don't have to keep these things
-    around when trying to match
-    """
-    if 'id' in d: del d['id']
-    return d
-
-class FiltersCase(common.TransactionCase):
-    def build(self, model, *args):
-        Model = self.registry(model)
-        for vars in args:
-            Model.create(self.cr, common.ADMIN_USER_ID, vars, {})
-
-class TestGetFilters(FiltersCase):
-    def setUp(self):
-        super(TestGetFilters, self).setUp()
-        self.USER = self.registry('res.users').name_search(self.cr, self.uid, 'demo')[0]
-        self.USER_ID = self.USER[0]
-
-    def test_own_filters(self):
-        self.build(
-            'ir.filters',
-            dict(name='a', user_id=self.USER_ID, model_id='ir.filters'),
-            dict(name='b', user_id=self.USER_ID, model_id='ir.filters'),
-            dict(name='c', user_id=self.USER_ID, model_id='ir.filters'),
-            dict(name='d', user_id=self.USER_ID, model_id='ir.filters'))
-
-        filters = self.registry('ir.filters').get_filters(
-            self.cr, self.USER_ID, 'ir.filters')
-
-        self.assertItemsEqual(map(noid, filters), [
-            dict(name='a', is_default=False, user_id=self.USER, domain='[]', context='{}'),
-            dict(name='b', is_default=False, user_id=self.USER, domain='[]', context='{}'),
-            dict(name='c', is_default=False, user_id=self.USER, domain='[]', context='{}'),
-            dict(name='d', is_default=False, user_id=self.USER, domain='[]', context='{}'),
-        ])
-
-    def test_global_filters(self):
-        self.build(
-            'ir.filters',
-            dict(name='a', user_id=False, model_id='ir.filters'),
-            dict(name='b', user_id=False, model_id='ir.filters'),
-            dict(name='c', user_id=False, model_id='ir.filters'),
-            dict(name='d', user_id=False, model_id='ir.filters'),
-        )
-
-        filters = self.registry('ir.filters').get_filters(
-            self.cr, self.USER_ID, 'ir.filters')
-
-        self.assertItemsEqual(map(noid, filters), [
-            dict(name='a', is_default=False, user_id=False, domain='[]', context='{}'),
-            dict(name='b', is_default=False, user_id=False, domain='[]', context='{}'),
-            dict(name='c', is_default=False, user_id=False, domain='[]', context='{}'),
-            dict(name='d', is_default=False, user_id=False, domain='[]', context='{}'),
-        ])
-
-    def test_no_third_party_filters(self):
-        self.build(
-            'ir.filters',
-            dict(name='a', user_id=False, model_id='ir.filters'),
-            dict(name='b', user_id=common.ADMIN_USER_ID, model_id='ir.filters'),
-            dict(name='c', user_id=self.USER_ID, model_id='ir.filters'),
-            dict(name='d', user_id=common.ADMIN_USER_ID, model_id='ir.filters')  )
-
-        filters = self.registry('ir.filters').get_filters(
-            self.cr, self.USER_ID, 'ir.filters')
-
-        self.assertItemsEqual(map(noid, filters), [
-            dict(name='a', is_default=False, user_id=False, domain='[]', context='{}'),
-            dict(name='c', is_default=False, user_id=self.USER, domain='[]', context='{}'),
-        ])
-
-class TestOwnDefaults(FiltersCase):
-    def setUp(self):
-        super(TestOwnDefaults, self).setUp()
-        self.USER = self.registry('res.users').name_search(self.cr, self.uid, 'demo')[0]
-        self.USER_ID = self.USER[0]                 
-
-    def test_new_no_filter(self):
-        """
-        When creating a @is_default filter with no existing filter, that new
-        filter gets the default flag
-        """
-        Filters = self.registry('ir.filters')
-        Filters.create_or_replace(self.cr, self.USER_ID, {
-            'name': 'a',
-            'model_id': 'ir.filters',
-            'user_id': self.USER_ID,
-            'is_default': True,
-        })
-        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
-
-        self.assertItemsEqual(map(noid, filters), [
-            dict(name='a', user_id=self.USER, is_default=True,
-                 domain='[]', context='{}')
-        ])
-
-    def test_new_filter_not_default(self):
-        """
-        When creating a @is_default filter with existing non-default filters,
-        the new filter gets the flag
-        """
-        self.build(
-            'ir.filters',
-            dict(name='a', user_id=self.USER_ID, model_id='ir.filters'),
-            dict(name='b', user_id=self.USER_ID, model_id='ir.filters'),
-        )
-
-        Filters = self.registry('ir.filters')
-        Filters.create_or_replace(self.cr, self.USER_ID, {
-            'name': 'c',
-            'model_id': 'ir.filters',
-            'user_id': self.USER_ID,
-            'is_default': True,
-        })
-        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
-
-        self.assertItemsEqual(map(noid, filters), [
-            dict(name='a', user_id=self.USER, is_default=False, domain='[]', context='{}'),
-            dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}'),
-            dict(name='c', user_id=self.USER, is_default=True, domain='[]', context='{}'),
-        ])
-
-    def test_new_filter_existing_default(self):
-        """
-        When creating a @is_default filter where an existing filter is already
-        @is_default, the flag should be *moved* from the old to the new filter
-        """
-        self.build(
-            'ir.filters',
-            dict(name='a', user_id=self.USER_ID, model_id='ir.filters'),
-            dict(name='b', is_default=True, user_id=self.USER_ID, model_id='ir.filters'),
-        )
-
-        Filters = self.registry('ir.filters')
-        Filters.create_or_replace(self.cr, self.USER_ID, {
-            'name': 'c',
-            'model_id': 'ir.filters',
-            'user_id': self.USER_ID,
-            'is_default': True,
-        })
-        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
-
-        self.assertItemsEqual(map(noid, filters), [
-            dict(name='a', user_id=self.USER, is_default=False, domain='[]', context='{}'),
-            dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}'),
-            dict(name='c', user_id=self.USER, is_default=True, domain='[]', context='{}'),
-        ])
-
-    def test_update_filter_set_default(self):
-        """
-        When updating an existing filter to @is_default, if an other filter
-        already has the flag the flag should be moved
-        """
-        self.build(
-            'ir.filters',
-            dict(name='a', user_id=self.USER_ID, model_id='ir.filters'),
-            dict(name='b', is_default=True, user_id=self.USER_ID, model_id='ir.filters'),
-        )
-
-        Filters = self.registry('ir.filters')
-        Filters.create_or_replace(self.cr, self.USER_ID, {
-            'name': 'a',
-            'model_id': 'ir.filters',
-            'user_id': self.USER_ID,
-            'is_default': True,
-        })
-        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
-
-        self.assertItemsEqual(map(noid, filters), [
-            dict(name='a', user_id=self.USER, is_default=True, domain='[]', context='{}'),
-            dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}'),
-        ])
-
-class TestGlobalDefaults(FiltersCase):
-    def setUp(self):
-        super(TestGlobalDefaults, self).setUp()
-        self.USER = self.registry('res.users').name_search(self.cr, self.uid, 'demo')[0]
-        self.USER_ID = self.USER[0]
-
-    def test_new_filter_not_default(self):
-        """
-        When creating a @is_default filter with existing non-default filters,
-        the new filter gets the flag
-        """
-        self.build(
-            'ir.filters',
-            dict(name='a', user_id=False, model_id='ir.filters'),
-            dict(name='b', user_id=False, model_id='ir.filters'),
-        )
-
-        Filters = self.registry('ir.filters')
-        Filters.create_or_replace(self.cr, self.USER_ID, {
-            'name': 'c',
-            'model_id': 'ir.filters',
-            'user_id': False,
-            'is_default': True,
-        })
-        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
-
-        self.assertItemsEqual(map(noid, filters), [
-            dict(name='a', user_id=False, is_default=False, domain='[]', context='{}'),
-            dict(name='b', user_id=False, is_default=False, domain='[]', context='{}'),
-            dict(name='c', user_id=False, is_default=True, domain='[]', context='{}'),
-        ])
-
-    def test_new_filter_existing_default(self):
-        """
-        When creating a @is_default filter where an existing filter is already
-        @is_default, an error should be generated
-        """
-        self.build(
-            'ir.filters',
-            dict(name='a', user_id=False, model_id='ir.filters'),
-            dict(name='b', is_default=True, user_id=False, model_id='ir.filters'),
-        )
-
-        Filters = self.registry('ir.filters')
-        with self.assertRaises(exceptions.Warning):
-            Filters.create_or_replace(self.cr, self.USER_ID, {
-                'name': 'c',
-                'model_id': 'ir.filters',
-                'user_id': False,
-                'is_default': True,
-            })
-
-    def test_update_filter_set_default(self):
-        """
-        When updating an existing filter to @is_default, if an other filter
-        already has the flag an error should be generated
-        """
-        self.build(
-            'ir.filters',
-            dict(name='a', user_id=False, model_id='ir.filters'),
-            dict(name='b', is_default=True, user_id=False, model_id='ir.filters'),
-        )
-
-        Filters = self.registry('ir.filters')
-
-        with self.assertRaises(exceptions.Warning):
-            Filters.create_or_replace(self.cr, self.USER_ID, {
-                'name': 'a',
-                'model_id': 'ir.filters',
-                'user_id': False,
-                'is_default': True,
-            })
-
-    def test_update_default_filter(self):
-        """
-        Replacing the current default global filter should not generate any error
-        """
-        self.build(
-            'ir.filters',
-            dict(name='a', user_id=False, model_id='ir.filters'),
-            dict(name='b', is_default=True, user_id=False, model_id='ir.filters'),
-        )
-
-        Filters = self.registry('ir.filters')
-        context_value = "{'some_key': True}"
-        Filters.create_or_replace(self.cr, self.USER_ID, {
-            'name': 'b',
-            'model_id': 'ir.filters',
-            'user_id': False,
-            'context': context_value,
-            'is_default': True,
-        })
-        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
-
-        self.assertItemsEqual(map(noid, filters), [
-            dict(name='a', user_id=False, is_default=False, domain='[]', context='{}'),
-            dict(name='b', user_id=False, is_default=True, domain='[]', context=context_value),
-        ])
diff --git a/openerp/tests/test_ir_sequence.py b/openerp/tests/test_ir_sequence.py
deleted file mode 100644 (file)
index cbb1a34..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-# -*- coding: utf-8 -*-
-# Run with one of these commands:
-#    > OPENERP_ADDONS_PATH='../../addons/trunk' OPENERP_PORT=8069 \
-#      OPENERP_DATABASE=yy PYTHONPATH=. python tests/test_ir_sequence.py
-#    > OPENERP_ADDONS_PATH='../../addons/trunk' OPENERP_PORT=8069 \
-#      OPENERP_DATABASE=yy nosetests tests/test_ir_sequence.py
-#    > OPENERP_ADDONS_PATH='../../../addons/trunk' OPENERP_PORT=8069 \
-#      OPENERP_DATABASE=yy PYTHONPATH=../:. unit2 test_ir_sequence
-# This assume an existing database.
-import psycopg2
-import unittest2
-
-import openerp
-import common
-
-DB = common.DB
-ADMIN_USER_ID = common.ADMIN_USER_ID
-
-def registry(model):
-    return openerp.modules.registry.RegistryManager.get(DB)[model]
-
-def cursor():
-    return openerp.modules.registry.RegistryManager.get(DB).db.cursor()
-
-
-def drop_sequence(code):
-    cr = cursor()
-    for model in ['ir.sequence', 'ir.sequence.type']:
-        s = registry(model)
-        ids = s.search(cr, ADMIN_USER_ID, [('code', '=', code)])
-        s.unlink(cr, ADMIN_USER_ID, ids)
-    cr.commit()
-    cr.close()
-
-class test_ir_sequence_standard(unittest2.TestCase):
-    """ A few tests for a 'Standard' (i.e. PostgreSQL) sequence. """
-
-    def test_ir_sequence_create(self):
-        """ Try to create a sequence object. """
-        cr = cursor()
-        d = dict(code='test_sequence_type', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        d = dict(code='test_sequence_type', name='Test sequence')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        cr.commit()
-        cr.close()
-
-    def test_ir_sequence_search(self):
-        """ Try a search. """
-        cr = cursor()
-        ids = registry('ir.sequence').search(cr, ADMIN_USER_ID, [], {})
-        assert ids
-        cr.commit()
-        cr.close()
-
-    def test_ir_sequence_draw(self):
-        """ Try to draw a number. """
-        cr = cursor()
-        n = registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type', {})
-        assert n
-        cr.commit()
-        cr.close()
-
-    def test_ir_sequence_draw_twice(self):
-        """ Try to draw a number from two transactions. """
-        cr0 = cursor()
-        cr1 = cursor()
-        n0 = registry('ir.sequence').next_by_code(cr0, ADMIN_USER_ID, 'test_sequence_type', {})
-        assert n0
-        n1 = registry('ir.sequence').next_by_code(cr1, ADMIN_USER_ID, 'test_sequence_type', {})
-        assert n1
-        cr0.commit()
-        cr1.commit()
-        cr0.close()
-        cr1.close()
-
-    @classmethod
-    def tearDownClass(cls):
-        drop_sequence('test_sequence_type')
-
-class test_ir_sequence_no_gap(unittest2.TestCase):
-    """ Copy of the previous tests for a 'No gap' sequence. """
-
-    def test_ir_sequence_create_no_gap(self):
-        """ Try to create a sequence object. """
-        cr = cursor()
-        d = dict(code='test_sequence_type_2', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        d = dict(code='test_sequence_type_2', name='Test sequence',
-            implementation='no_gap')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        cr.commit()
-        cr.close()
-
-    def test_ir_sequence_draw_no_gap(self):
-        """ Try to draw a number. """
-        cr = cursor()
-        n = registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_2', {})
-        assert n
-        cr.commit()
-        cr.close()
-
-    def test_ir_sequence_draw_twice_no_gap(self):
-        """ Try to draw a number from two transactions.
-        This is expected to not work.
-        """
-        cr0 = cursor()
-        cr1 = cursor()
-        cr1._default_log_exceptions = False # Prevent logging a traceback
-        msg_re = '^could not obtain lock on row in relation "ir_sequence"$'
-        with self.assertRaisesRegexp(psycopg2.OperationalError, msg_re):
-            n0 = registry('ir.sequence').next_by_code(cr0, ADMIN_USER_ID, 'test_sequence_type_2', {})
-            assert n0
-            n1 = registry('ir.sequence').next_by_code(cr1, ADMIN_USER_ID, 'test_sequence_type_2', {})
-        cr0.close()
-        cr1.close()
-
-    @classmethod
-    def tearDownClass(cls):
-        drop_sequence('test_sequence_type_2')
-
-class test_ir_sequence_change_implementation(unittest2.TestCase):
-    """ Create sequence objects and change their ``implementation`` field. """
-
-    def test_ir_sequence_1_create(self):
-        """ Try to create a sequence object. """
-        cr = cursor()
-        d = dict(code='test_sequence_type_3', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        d = dict(code='test_sequence_type_3', name='Test sequence')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        d = dict(code='test_sequence_type_4', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        d = dict(code='test_sequence_type_4', name='Test sequence',
-            implementation='no_gap')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        cr.commit()
-        cr.close()
-
-    def test_ir_sequence_2_write(self):
-        cr = cursor()
-        ids = registry('ir.sequence').search(cr, ADMIN_USER_ID,
-            [('code', 'in', ['test_sequence_type_3', 'test_sequence_type_4'])], {})
-        registry('ir.sequence').write(cr, ADMIN_USER_ID, ids,
-            {'implementation': 'standard'}, {})
-        registry('ir.sequence').write(cr, ADMIN_USER_ID, ids,
-            {'implementation': 'no_gap'}, {})
-        cr.commit()
-        cr.close()
-
-    def test_ir_sequence_3_unlink(self):
-        cr = cursor()
-        ids = registry('ir.sequence').search(cr, ADMIN_USER_ID,
-            [('code', 'in', ['test_sequence_type_3', 'test_sequence_type_4'])], {})
-        registry('ir.sequence').unlink(cr, ADMIN_USER_ID, ids, {})
-        cr.commit()
-        cr.close()
-
-    @classmethod
-    def tearDownClass(cls):
-        drop_sequence('test_sequence_type_3')
-        drop_sequence('test_sequence_type_4')
-
-class test_ir_sequence_generate(unittest2.TestCase):
-    """ Create sequence objects and generate some values. """
-
-    def test_ir_sequence_create(self):
-        """ Try to create a sequence object. """
-        cr = cursor()
-        d = dict(code='test_sequence_type_5', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        d = dict(code='test_sequence_type_5', name='Test sequence')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        cr.commit()
-        cr.close()
-
-        cr = cursor()
-        f = lambda *a: registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_5', {})
-        assert all(str(x) == f() for x in xrange(1,10))
-        cr.commit()
-        cr.close()
-
-    def test_ir_sequence_create_no_gap(self):
-        """ Try to create a sequence object. """
-        cr = cursor()
-        d = dict(code='test_sequence_type_6', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        d = dict(code='test_sequence_type_6', name='Test sequence')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
-        assert c
-        cr.commit()
-        cr.close()
-
-        cr = cursor()
-        f = lambda *a: registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_6', {})
-        assert all(str(x) == f() for x in xrange(1,10))
-        cr.commit()
-        cr.close()
-
-    @classmethod
-    def tearDownClass(cls):
-        drop_sequence('test_sequence_type_5')
-        drop_sequence('test_sequence_type_6')
-
-
-if __name__ == '__main__':
-    unittest2.main()
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/test_mail.py b/openerp/tests/test_mail.py
deleted file mode 100755 (executable)
index d99a95a..0000000
+++ /dev/null
@@ -1,380 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# This test can be run stand-alone with something like:
-# > PYTHONPATH=. python2 openerp/tests/test_misc.py
-##############################################################################
-#
-#    OpenERP, Open Source Business Applications
-#    Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
-#
-#    This program is free software: you can redistribute it and/or modify
-#    it under the terms of the GNU Affero General Public License as
-#    published by the Free Software Foundation, either version 3 of the
-#    License, or (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU Affero General Public License for more details.
-#
-#    You should have received a copy of the GNU Affero General Public License
-#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-
-import unittest2
-
-from lxml import etree
-
-from openerp.tests import test_mail_examples
-from openerp.tools import html_sanitize, html_email_clean, append_content_to_html, plaintext2html, email_split
-
-
-class TestSanitizer(unittest2.TestCase):
-    """ Test the html sanitizer that filters html to remove unwanted attributes """
-
-    def test_basic_sanitizer(self):
-        cases = [
-            ("yop", "<p>yop</p>"),  # simple
-            ("lala<p>yop</p>xxx", "<p>lala</p><p>yop</p>xxx"),  # trailing text
-            ("Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci",
-                u"<p>Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci</p>"),  # unicode
-        ]
-        for content, expected in cases:
-            html = html_sanitize(content)
-            self.assertEqual(html, expected, 'html_sanitize is broken')
-
-    def test_evil_malicious_code(self):
-        # taken from https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Tests
-        cases = [
-            ("<IMG SRC=javascript:alert('XSS')>"),  # no quotes and semicolons
-            ("<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>"),  # UTF-8 Unicode encoding
-            ("<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>"),  # hex encoding
-            ("<IMG SRC=\"jav&#x0D;ascript:alert('XSS');\">"),  # embedded carriage return
-            ("<IMG SRC=\"jav&#x0A;ascript:alert('XSS');\">"),  # embedded newline
-            ("<IMG SRC=\"jav   ascript:alert('XSS');\">"),  # embedded tab
-            ("<IMG SRC=\"jav&#x09;ascript:alert('XSS');\">"),  # embedded encoded tab
-            ("<IMG SRC=\" &#14;  javascript:alert('XSS');\">"),  # spaces and meta-characters
-            ("<IMG SRC=\"javascript:alert('XSS')\""),  # half-open html
-            ("<IMG \"\"\"><SCRIPT>alert(\"XSS\")</SCRIPT>\">"),  # malformed tag
-            ("<SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>"),  # non-alpha-non-digits
-            ("<SCRIPT/SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>"),  # non-alpha-non-digits
-            ("<<SCRIPT>alert(\"XSS\");//<</SCRIPT>"),  # extraneous open brackets
-            ("<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >"),  # non-closing script tags
-            ("<INPUT TYPE=\"IMAGE\" SRC=\"javascript:alert('XSS');\">"),  # input image
-            ("<BODY BACKGROUND=\"javascript:alert('XSS')\">"),  # body image
-            ("<IMG DYNSRC=\"javascript:alert('XSS')\">"),  # img dynsrc
-            ("<IMG LOWSRC=\"javascript:alert('XSS')\">"),  # img lowsrc
-            ("<TABLE BACKGROUND=\"javascript:alert('XSS')\">"),  # table
-            ("<TABLE><TD BACKGROUND=\"javascript:alert('XSS')\">"),  # td
-            ("<DIV STYLE=\"background-image: url(javascript:alert('XSS'))\">"),  # div background
-            ("<DIV STYLE=\"background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029\">"),  # div background with unicoded exploit
-            ("<DIV STYLE=\"background-image: url(&#1;javascript:alert('XSS'))\">"),  # div background + extra characters
-            ("<IMG SRC='vbscript:msgbox(\"XSS\")'>"),  # VBscrip in an image
-            ("<BODY ONLOAD=alert('XSS')>"),  # event handler
-            ("<BR SIZE=\"&{alert('XSS')}\>"),  # & javascript includes
-            ("<LINK REL=\"stylesheet\" HREF=\"javascript:alert('XSS');\">"),  # style sheet
-            ("<LINK REL=\"stylesheet\" HREF=\"http://ha.ckers.org/xss.css\">"),  # remote style sheet
-            ("<STYLE>@import'http://ha.ckers.org/xss.css';</STYLE>"),  # remote style sheet 2
-            ("<META HTTP-EQUIV=\"Link\" Content=\"<http://ha.ckers.org/xss.css>; REL=stylesheet\">"),  # remote style sheet 3
-            ("<STYLE>BODY{-moz-binding:url(\"http://ha.ckers.org/xssmoz.xml#xss\")}</STYLE>"),  # remote style sheet 4
-            ("<IMG STYLE=\"xss:expr/*XSS*/ession(alert('XSS'))\">"),  # style attribute using a comment to break up expression
-        ]
-        for content in cases:
-            html = html_sanitize(content)
-            self.assertNotIn('javascript', html, 'html_sanitize did not remove a malicious javascript')
-            self.assertTrue('ha.ckers.org' not in html or 'http://ha.ckers.org/xss.css' in html, 'html_sanitize did not remove a malicious code in %s (%s)' % (content, html))
-
-        content = "<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->"  # down-level hidden block
-        self.assertEquals(html_sanitize(content, silent=False), '')
-
-    def test_html(self):
-        sanitized_html = html_sanitize(test_mail_examples.MISC_HTML_SOURCE)
-        for tag in ['<div', '<b', '<i', '<u', '<strike', '<li', '<blockquote', '<a href']:
-            self.assertIn(tag, sanitized_html, 'html_sanitize stripped too much of original html')
-        for attr in ['javascript']:
-            self.assertNotIn(attr, sanitized_html, 'html_sanitize did not remove enough unwanted attributes')
-
-        emails = [("Charles <charles.bidule@truc.fr>", "Charles &lt;charles.bidule@truc.fr&gt;"),
-                ("Dupuis <'tr/-: ${dupuis#$'@truc.baz.fr>", "Dupuis &lt;'tr/-: ${dupuis#$'@truc.baz.fr&gt;"),
-                ("Technical <service/technical+2@open.com>", "Technical &lt;service/technical+2@open.com&gt;"),
-                ("Div nico <div-nico@open.com>", "Div nico &lt;div-nico@open.com&gt;")]
-        for email in emails:
-            self.assertIn(email[1], html_sanitize(email[0]), 'html_sanitize stripped emails of original html')
-
-    def test_edi_source(self):
-        html = html_sanitize(test_mail_examples.EDI_LIKE_HTML_SOURCE)
-        self.assertIn('div style="font-family: \'Lucica Grande\', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF;', html,
-            'html_sanitize removed valid style attribute')
-        self.assertIn('<span style="color: #222; margin-bottom: 5px; display: block; ">', html,
-            'html_sanitize removed valid style attribute')
-        self.assertIn('img class="oe_edi_paypal_button" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"', html,
-            'html_sanitize removed valid img')
-        self.assertNotIn('</body></html>', html, 'html_sanitize did not remove extra closing tags')
-
-
-class TestCleaner(unittest2.TestCase):
-    """ Test the email cleaner function that filters the content of incoming emails """
-
-    def test_00_basic_text(self):
-        """ html_email_clean test for signatures """
-        test_data = [
-            (
-                """This is Sparta!\n--\nAdministrator\n+9988776655""",
-                ['This is Sparta!'],
-                ['Administrator', '9988776655']
-            ), (
-                """<p>--\nAdministrator</p>""",
-                [],
-                ['--', 'Administrator']
-            ), (
-                """<p>This is Sparta!\n---\nAdministrator</p>""",
-                ['This is Sparta!'],
-                ['---', 'Administrator']
-            ), (
-                """<p>--<br>Administrator</p>""",
-                [],
-                []
-            ), (
-                """<p>This is Sparta!<br/>--<br>Administrator</p>""",
-                ['This is Sparta!'],
-                []
-            ), (
-                """This is Sparta!\n>Ah bon ?\nCertes\n> Chouette !\nClair""",
-                ['This is Sparta!', 'Certes', 'Clair'],
-                ['Ah bon', 'Chouette']
-            )
-        ]
-        for test, in_lst, out_lst in test_data:
-            new_html = html_email_clean(test, remove=True)
-            for text in in_lst:
-                self.assertIn(text, new_html, 'html_email_cleaner wrongly removed content')
-            for text in out_lst:
-                self.assertNotIn(text, new_html, 'html_email_cleaner did not remove unwanted content')
-
-    def test_05_shorten(self):
-        # TEST: shorten length
-        test_str = '''<div>
-        <span>
-        </span>
-        <p>Hello, <span>Raoul</span> 
-    <bold>You</bold> are 
-    pretty</p>
-<span>Really</span>
-</div>
-'''
-        # shorten at 'H' of Hello -> should shorten after Hello,
-        html = html_email_clean(test_str, shorten=True, max_length=1, remove=True)
-        self.assertIn('Hello,', html, 'html_email_cleaner: shorten error or too short')
-        self.assertNotIn('Raoul', html, 'html_email_cleaner: shorten error or too long')
-        self.assertIn('read more', html, 'html_email_cleaner: shorten error about read more inclusion')
-        # shorten at 'are' -> should shorten after are
-        html = html_email_clean(test_str, shorten=True, max_length=17, remove=True)
-        self.assertIn('Hello,', html, 'html_email_cleaner: shorten error or too short')
-        self.assertIn('Raoul', html, 'html_email_cleaner: shorten error or too short')
-        self.assertIn('are', html, 'html_email_cleaner: shorten error or too short')
-        self.assertNotIn('pretty', html, 'html_email_cleaner: shorten error or too long')
-        self.assertNotIn('Really', html, 'html_email_cleaner: shorten error or too long')
-        self.assertIn('read more', html, 'html_email_cleaner: shorten error about read more inclusion')
-
-        # TEST: shorten in quote
-        test_str = '''<div> Blahble         
-            bluih      blouh   
-        <blockquote>This is a quote
-        <span>And this is quite a long quote, after all.</span>
-        </blockquote>
-</div>'''
-        # shorten in the quote
-        html = html_email_clean(test_str, shorten=True, max_length=25, remove=True)
-        self.assertIn('Blahble', html, 'html_email_cleaner: shorten error or too short')
-        self.assertIn('bluih', html, 'html_email_cleaner: shorten error or too short')
-        self.assertIn('blouh', html, 'html_email_cleaner: shorten error or too short')
-        self.assertNotIn('quote', html, 'html_email_cleaner: shorten error or too long')
-        self.assertIn('read more', html, 'html_email_cleaner: shorten error about read more inclusion')
-        # shorten in second word
-        html = html_email_clean(test_str, shorten=True, max_length=9, remove=True)
-        self.assertIn('Blahble', html, 'html_email_cleaner: shorten error or too short')
-        self.assertIn('bluih', html, 'html_email_cleaner: shorten error or too short')
-        self.assertNotIn('blouh', html, 'html_email_cleaner: shorten error or too short')
-        self.assertNotIn('quote', html, 'html_email_cleaner: shorten error or too long')
-        self.assertIn('read more', html, 'html_email_cleaner: shorten error about read more inclusion')
-        # shorten waaay too large
-        html = html_email_clean(test_str, shorten=True, max_length=900, remove=True)
-        self.assertIn('Blahble', html, 'html_email_cleaner: shorten error or too short')
-        self.assertIn('bluih', html, 'html_email_cleaner: shorten error or too short')
-        self.assertIn('blouh', html, 'html_email_cleaner: shorten error or too short')
-        self.assertNotIn('quote', html, 'html_email_cleaner: shorten error or too long')
-
-    def test_10_email_text(self):
-        """ html_email_clean test for text-based emails """
-        new_html = html_email_clean(test_mail_examples.TEXT_1, remove=True)
-        for ext in test_mail_examples.TEXT_1_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.TEXT_1_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
-
-        new_html = html_email_clean(test_mail_examples.TEXT_2, remove=True)
-        for ext in test_mail_examples.TEXT_2_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.TEXT_2_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
-
-    def test_20_email_html(self):
-        new_html = html_email_clean(test_mail_examples.HTML_1, remove=True)
-        for ext in test_mail_examples.HTML_1_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.HTML_1_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
-
-        new_html = html_email_clean(test_mail_examples.HTML_2, remove=True)
-        for ext in test_mail_examples.HTML_2_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.HTML_2_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
-
-        # --- MAIL ORIGINAL --- -> can't parse this one currently, too much language-dependent
-        # new_html = html_email_clean(test_mail_examples.HTML_3, remove=False)
-        # for ext in test_mail_examples.HTML_3_IN:
-        #     self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        # for ext in test_mail_examples.HTML_3_OUT:
-        #     self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
-
-    def test_30_email_msoffice(self):
-        new_html = html_email_clean(test_mail_examples.MSOFFICE_1, remove=True)
-        for ext in test_mail_examples.MSOFFICE_1_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.MSOFFICE_1_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
-
-        new_html = html_email_clean(test_mail_examples.MSOFFICE_2, remove=True)
-        for ext in test_mail_examples.MSOFFICE_2_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.MSOFFICE_2_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
-
-        new_html = html_email_clean(test_mail_examples.MSOFFICE_3, remove=True)
-        for ext in test_mail_examples.MSOFFICE_3_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.MSOFFICE_3_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
-
-    def test_40_email_hotmail(self):
-        new_html = html_email_clean(test_mail_examples.HOTMAIL_1, remove=True)
-        for ext in test_mail_examples.HOTMAIL_1_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.HOTMAIL_1_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
-
-    def test_50_email_gmail(self):
-        new_html = html_email_clean(test_mail_examples.GMAIL_1, remove=True)
-        for ext in test_mail_examples.GMAIL_1_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.GMAIL_1_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
-
-    def test_60_email_thunderbird(self):
-        new_html = html_email_clean(test_mail_examples.THUNDERBIRD_1, remove=True)
-        for ext in test_mail_examples.THUNDERBIRD_1_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.THUNDERBIRD_1_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')
-
-    def test_70_read_more_and_shorten(self):
-        expand_options = {
-            'oe_expand_container_class': 'span_class',
-            'oe_expand_container_content': 'Herbert Einstein',
-            'oe_expand_separator_node': 'br_lapin',
-            'oe_expand_a_class': 'a_class',
-            'oe_expand_a_content': 'read mee',
-        }
-        new_html = html_email_clean(test_mail_examples.OERP_WEBSITE_HTML_1, remove=True, shorten=True, max_length=100, expand_options=expand_options)
-        for ext in test_mail_examples.OERP_WEBSITE_HTML_1_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.OERP_WEBSITE_HTML_1_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase overlimit content')
-        for ext in ['<span class="span_class">Herbert Einstein<br_lapin></br_lapin><a href="#" class="a_class">read mee</a></span>']:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly take into account specific expand options')
-
-        new_html = html_email_clean(test_mail_examples.OERP_WEBSITE_HTML_2, remove=True, shorten=True, max_length=200, expand_options=expand_options, protect_sections=False)
-        for ext in test_mail_examples.OERP_WEBSITE_HTML_2_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.OERP_WEBSITE_HTML_2_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase overlimit content')
-        for ext in ['<span class="span_class">Herbert Einstein<br_lapin></br_lapin><a href="#" class="a_class">read mee</a></span>']:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly take into account specific expand options')
-
-        new_html = html_email_clean(test_mail_examples.OERP_WEBSITE_HTML_2, remove=True, shorten=True, max_length=200, expand_options=expand_options, protect_sections=True)
-        for ext in test_mail_examples.OERP_WEBSITE_HTML_2_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed not quoted content')
-        for ext in test_mail_examples.OERP_WEBSITE_HTML_2_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase overlimit content')
-        for ext in [
-                '<span class="span_class">Herbert Einstein<br_lapin></br_lapin><a href="#" class="a_class">read mee</a></span>',
-                'tasks using the gantt chart and control deadlines']:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly take into account specific expand options')
-
-    def test_70_read_more(self):
-        new_html = html_email_clean(test_mail_examples.BUG1, remove=True, shorten=True, max_length=100)
-        for ext in test_mail_examples.BUG_1_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed valid content')
-        for ext in test_mail_examples.BUG_1_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not removed invalid content')
-
-        new_html = html_email_clean(test_mail_examples.BUG2, remove=True, shorten=True, max_length=250)
-        for ext in test_mail_examples.BUG_2_IN:
-            self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed valid content')
-        for ext in test_mail_examples.BUG_2_OUT:
-            self.assertNotIn(ext, new_html, 'html_email_cleaner did not removed invalid content')
-
-    def test_90_misc(self):
-        # False boolean for text must return empty string
-        new_html = html_email_clean(False)
-        self.assertEqual(new_html, False, 'html_email_cleaner did change a False in an other value.')
-
-        # Message with xml and doctype tags don't crash
-        new_html = html_email_clean(u'<?xml version="1.0" encoding="iso-8859-1"?>\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n <head>\n  <title>404 - Not Found</title>\n </head>\n <body>\n  <h1>404 - Not Found</h1>\n </body>\n</html>\n')
-        self.assertNotIn('encoding', new_html, 'html_email_cleaner did not remove correctly encoding attributes')
-
-
-class TestHtmlTools(unittest2.TestCase):
-    """ Test some of our generic utility functions about html """
-
-    def test_plaintext2html(self):
-        cases = [
-            ("First \nSecond \nThird\n \nParagraph\n\r--\nSignature paragraph", 'div',
-             "<div><p>First <br/>Second <br/>Third</p><p>Paragraph</p><p>--<br/>Signature paragraph</p></div>"),
-            ("First<p>It should be escaped</p>\nSignature", False,
-             "<p>First&lt;p&gt;It should be escaped&lt;/p&gt;<br/>Signature</p>")
-        ]
-        for content, container_tag, expected in cases:
-            html = plaintext2html(content, container_tag)
-            self.assertEqual(html, expected, 'plaintext2html is broken')
-
-    def test_append_to_html(self):
-        test_samples = [
-            ('<!DOCTYPE...><HTML encoding="blah">some <b>content</b></HtMl>', '--\nYours truly', True, True, False,
-             '<!DOCTYPE...><html encoding="blah">some <b>content</b>\n<pre>--\nYours truly</pre>\n</html>'),
-            ('<!DOCTYPE...><HTML encoding="blah">some <b>content</b></HtMl>', '--\nYours truly', True, False, False,
-             '<!DOCTYPE...><html encoding="blah">some <b>content</b>\n<p>--<br/>Yours truly</p>\n</html>'),
-            ('<html><body>some <b>content</b></body></html>', '<!DOCTYPE...>\n<html><body>\n<p>--</p>\n<p>Yours truly</p>\n</body>\n</html>', False, False, False,
-             '<html><body>some <b>content</b>\n\n\n<p>--</p>\n<p>Yours truly</p>\n\n\n</body></html>'),
-        ]
-        for html, content, plaintext_flag, preserve_flag, container_tag, expected in test_samples:
-            self.assertEqual(append_content_to_html(html, content, plaintext_flag, preserve_flag, container_tag), expected, 'append_content_to_html is broken')
-
-class TestEmailTools(unittest2.TestCase):
-    """ Test some of our generic utility functions for emails """
-
-    def test_email_split(self):
-        cases = [
-            ("John <12345@gmail.com>", ['12345@gmail.com']), # regular form 
-            ("d@x; 1@2", ['d@x', '1@2']), # semi-colon + extra space
-            ("'(ss)' <123@gmail.com>, 'foo' <foo@bar>", ['123@gmail.com','foo@bar']), # comma + single-quoting
-            ('"john@gmail.com"<johnny@gmail.com>', ['johnny@gmail.com']), # double-quoting
-            ('"<jg>" <johnny@gmail.com>', ['johnny@gmail.com']), # double-quoting with brackets 
-        ]
-        for text, expected in cases:
-            self.assertEqual(email_split(text), expected, 'email_split is broken')
-
-if __name__ == '__main__':
-    unittest2.main()
diff --git a/openerp/tests/test_mail_examples.py b/openerp/tests/test_mail_examples.py
deleted file mode 100644 (file)
index 6886ce8..0000000
+++ /dev/null
@@ -1,1106 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-MISC_HTML_SOURCE = """
-<font size="2" style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; ">test1</font>
-<div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; font-size: 12px; font-style: normal; ">
-<b>test2</b></div><div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; font-size: 12px; ">
-<i>test3</i></div><div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; font-size: 12px; ">
-<u>test4</u></div><div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; font-size: 12px; ">
-<strike>test5</strike></div><div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; ">
-<font size="5">test6</font></div><div><ul><li><font color="#1f1f1f" face="monospace" size="2">test7</font></li><li>
-<font color="#1f1f1f" face="monospace" size="2">test8</font></li></ul><div><ol><li><font color="#1f1f1f" face="monospace" size="2">test9</font>
-</li><li><font color="#1f1f1f" face="monospace" size="2">test10</font></li></ol></div></div>
-<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><div><div><div><font color="#1f1f1f" face="monospace" size="2">
-test11</font></div></div></div></blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">
-<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><div><font color="#1f1f1f" face="monospace" size="2">
-test12</font></div><div><font color="#1f1f1f" face="monospace" size="2"><br></font></div></blockquote></blockquote>
-<font color="#1f1f1f" face="monospace" size="2"><a href="http://google.com">google</a></font>
-<a href="javascript:alert('malicious code')">test link</a>
-"""
-
-EDI_LIKE_HTML_SOURCE = """<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
-    <p>Hello ${object.partner_id.name},</p>
-    <p>A new invoice is available for you: </p>
-    <p style="border-left: 1px solid #8e0000; margin-left: 30px;">
-       &nbsp;&nbsp;<strong>REFERENCES</strong><br />
-       &nbsp;&nbsp;Invoice number: <strong>${object.number}</strong><br />
-       &nbsp;&nbsp;Invoice total: <strong>${object.amount_total} ${object.currency_id.name}</strong><br />
-       &nbsp;&nbsp;Invoice date: ${object.date_invoice}<br />
-       &nbsp;&nbsp;Order reference: ${object.origin}<br />
-       &nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a>
-    </p>
-    <br/>
-    <p>It is also possible to directly pay with Paypal:</p>
-    <a style="margin-left: 120px;" href="${object.paypal_url}">
-        <img class="oe_edi_paypal_button" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"/>
-    </a>
-    <br/>
-    <p>If you have any question, do not hesitate to contact us.</p>
-    <p>Thank you for choosing ${object.company_id.name or 'us'}!</p>
-    <br/>
-    <br/>
-    <div style="width: 375px; margin: 0px; padding: 0px; background-color: #8E0000; border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; background-repeat: repeat no-repeat;">
-        <h3 style="margin: 0px; padding: 2px 14px; font-size: 12px; color: #DDD;">
-            <strong style="text-transform:uppercase;">${object.company_id.name}</strong></h3>
-    </div>
-    <div style="width: 347px; margin: 0px; padding: 5px 14px; line-height: 16px; background-color: #F2F2F2;">
-        <span style="color: #222; margin-bottom: 5px; display: block; ">
-        ${object.company_id.street}<br/>
-        ${object.company_id.street2}<br/>
-        ${object.company_id.zip} ${object.company_id.city}<br/>
-        ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}<br/>
-        </span>
-        <div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
-            Phone:&nbsp; ${object.company_id.phone}
-        </div>
-        <div>
-            Web :&nbsp;<a href="${object.company_id.website}">${object.company_id.website}</a>
-        </div>
-    </div>
-</div></body></html>"""
-
-OERP_WEBSITE_HTML_1 = """
-<div>
-    <div class="container">
-        <div class="row">
-            <div class="col-md-12 text-center mt16 mb16" data-snippet-id="colmd">
-                <h2>OpenERP HR Features</h2>
-                <h3 class="text-muted">Manage your company most important asset: People</h3>
-            </div>
-            <div class="col-md-4" data-snippet-id="colmd">
-                <img class="img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg">
-                <h4 class="mt16">Streamline Recruitments</h4>
-                <p>Post job offers and keep track of each application received. Follow applicants in your recruitment process with the smart kanban view.</p>
-                <p>Save time by automating some communications with email templates. Resumes are indexed automatically, allowing you to easily find for specific profiles.</p>
-            </div>
-            <div class="col-md-4" data-snippet-id="colmd">
-                <img class="img-rounded img-responsive" src="/website/static/src/img/desert_thumb.jpg">
-                <h4 class="mt16">Enterprise Social Network</h4>
-                <p>Break down information silos. Share knowledge and best practices amongst all employees. Follow specific people or documents and join groups of interests to share expertise and documents.</p>
-                <p>Interact with your collegues in real time with live chat.</p>
-            </div>
-            <div class="col-md-4" data-snippet-id="colmd">
-                <img class="img-rounded img-responsive" src="/website/static/src/img/deers_thumb.jpg">
-                <h4 class="mt16">Leaves Management</h4>
-                <p>Keep track of the vacation days accrued by each employee. Employees enter their requests (paid holidays, sick leave, etc), for managers to approve and validate. It's all done in just a few clicks. The agenda of each employee is updated accordingly.</p>
-            </div>
-        </div>
-    </div>
-</div>"""
-
-OERP_WEBSITE_HTML_1_IN = [
-    'Manage your company most important asset: People',
-    'img class="img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg"',
-]
-OERP_WEBSITE_HTML_1_OUT = [
-    'Break down information silos.',
-    'Keep track of the vacation days accrued by each employee',
-    'img class="img-rounded img-responsive" src="/website/static/src/img/deers_thumb.jpg',
-]
-
-OERP_WEBSITE_HTML_2 = """
-<div class="mt16 cke_widget_editable cke_widget_element oe_editable oe_dirty" data-oe-model="blog.post" data-oe-id="6" data-oe-field="content" data-oe-type="html" data-oe-translate="0" data-oe-expression="blog_post.content" data-cke-widget-data="{}" data-cke-widget-keep-attr="0" data-widget="oeref" contenteditable="true" data-cke-widget-editable="text">
-    <section class="mt16 mb16" data-snippet-id="text-block">
-        <div class="container">
-            <div class="row">
-                <div class="col-md-12 text-center mt16 mb32" data-snippet-id="colmd">
-                    <h2>
-                        OpenERP Project Management
-                    </h2>
-                    <h3 class="text-muted">Infinitely flexible. Incredibly easy to use.</h3>
-                </div>
-                <div class="col-md-12 mb16 mt16" data-snippet-id="colmd">
-                    <p>
-                        OpenERP's <b>collaborative and realtime</b> project
-                        management helps your team get work done. Keep
-                        track of everything, from the big picture to the
-                        minute details, from the customer contract to the
-                        billing.
-                    </p><p>
-                        Organize projects around <b>your own processes</b>. Work
-                        on tasks and issues using the kanban view, schedule
-                        tasks using the gantt chart and control deadlines
-                        in the calendar view. Every project may have it's
-                        own stages allowing teams to optimize their job.
-                    </p>
-                </div>
-            </div>
-        </div>
-    </section>
-    <section class="" data-snippet-id="image-text">
-        <div class="container">
-            <div class="row">
-                <div class="col-md-6 mt16 mb16" data-snippet-id="colmd">
-                    <img class="img-responsive shadow" src="/website/static/src/img/image_text.jpg">
-                </div>
-                <div class="col-md-6 mt32" data-snippet-id="colmd">
-                    <h3>Manage Your Shops</h3>
-                    <p>
-                        OpenERP's Point of Sale introduces a super clean
-                        interface with no installation required that runs
-                        online and offline on modern hardwares.
-                    </p><p>
-                        It's full integration with the company inventory
-                        and accounting, gives you real time statistics and
-                        consolidations amongst all shops without the hassle
-                        of integrating several applications.
-                    </p>
-                </div>
-            </div>
-        </div>
-    </section>
-    <section class="" data-snippet-id="text-image">
-        <div class="container">
-            <div class="row">
-                <div class="col-md-6 mt32" data-snippet-id="colmd">
-                    <h3>Enterprise Social Network</h3>
-                    <p>
-                        Make every employee feel more connected and engaged
-                        with twitter-like features for your own company. Follow
-                        people, share best practices, 'like' top ideas, etc.
-                    </p><p>
-                        Connect with experts, follow what interests you, share
-                        documents and promote best practices with OpenERP
-                        Social application. Get work done with effective
-                        collaboration across departments, geographies
-                        and business applications.
-                    </p>
-                </div>
-                <div class="col-md-6 mt16 mb16" data-snippet-id="colmd">
-                    <img class="img-responsive shadow" src="/website/static/src/img/text_image.png">
-                </div>
-            </div>
-        </div>
-    </section><section class="" data-snippet-id="portfolio">
-        <div class="container">
-            <div class="row">
-                <div class="col-md-12 text-center mt16 mb32" data-snippet-id="colmd">
-                    <h2>Our Porfolio</h2>
-                    <h4 class="text-muted">More than 500 successful projects</h4>
-                </div>
-                <div class="col-md-4" data-snippet-id="colmd">
-                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/deers.jpg">
-                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg">
-                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg">
-                </div>
-                <div class="col-md-4" data-snippet-id="colmd">
-                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg">
-                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg">
-                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/deers.jpg">
-                </div>
-                <div class="col-md-4" data-snippet-id="colmd">
-                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/landscape.jpg">
-                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg">
-                    <img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg">
-                </div>
-            </div>
-        </div>
-    </section>
-</div>
-"""
-
-OERP_WEBSITE_HTML_2_IN = [
-    'management helps your team get work done',
-]
-OERP_WEBSITE_HTML_2_OUT = [
-    'Make every employee feel more connected',
-    'img class="img-responsive shadow" src="/website/static/src/img/text_image.png',
-]
-
-TEXT_1 = """I contact you about our meeting tomorrow. Here is the schedule I propose:
-9 AM: brainstorming about our new amazing business app
-9.45 AM: summary
-10 AM: meeting with Ignasse to present our app
-Is everything ok for you ?
---
-MySignature"""
-
-TEXT_1_IN = ["""I contact you about our meeting tomorrow. Here is the schedule I propose:
-9 AM: brainstorming about our new amazing business app
-9.45 AM: summary
-10 AM: meeting with Ignasse to present our app
-Is everything ok for you ?"""]
-TEXT_1_OUT = ["""--
-MySignature"""]
-
-TEXT_2 = """Salut Raoul!
-Le 28 oct. 2012 à 00:02, Raoul Grosbedon a écrit :
-
-> I contact you about our meeting tomorrow. Here is the schedule I propose: (quote)
-
-Of course. This seems viable.
-
-> 2012/10/27 Bert Tartopoils :
->> blahblahblah (quote)?
->> 
->> blahblahblah (quote)
->> 
->> Bert TARTOPOILS
->> bert.tartopoils@miam.miam
->> 
-> 
-> 
-> -- 
-> RaoulSignature
-
-Bert TARTOPOILS
-bert.tartopoils@miam.miam
-"""
-
-TEXT_2_IN = ["Salut Raoul!", "Of course. This seems viable."]
-TEXT_2_OUT = ["I contact you about our meeting tomorrow. Here is the schedule I propose: (quote)",
-    """> 2012/10/27 Bert Tartopoils :
->> blahblahblah (quote)?
->> 
->> blahblahblah (quote)
->> 
->> Bert TARTOPOILS
->> bert.tartopoils@miam.miam
->> 
-> 
-> 
-> -- 
-> RaoulSignature"""]
-
-HTML_1 = """<p>I contact you about our meeting for tomorrow. Here is the schedule I propose: (keep)
-9 AM: brainstorming about our new amazing business app
-9.45 AM: summary
-10 AM: meeting with Ignasse to present our app
-Is everything ok for you ?
---
-MySignature</p>"""
-
-HTML_1_IN = ["""I contact you about our meeting for tomorrow. Here is the schedule I propose: (keep)
-9 AM: brainstorming about our new amazing business app
-9.45 AM: summary
-10 AM: meeting with Ignasse to present our app
-Is everything ok for you ?"""]
-HTML_1_OUT = ["""--
-MySignature"""]
-
-HTML_2 = """<div>
-    <font><span>I contact you about our meeting for tomorrow. Here is the schedule I propose:</span></font>
-</div>
-<div>
-    <ul>
-        <li><span>9 AM: brainstorming about our new amazing business app</span></li>
-        <li><span>9.45 AM: summary</span></li>
-        <li><span>10 AM: meeting with Fabien to present our app</span></li>
-    </ul>
-</div>
-<div>
-    <font><span>Is everything ok for you ?</span></font>
-</div>"""
-
-HTML_2_IN = ["<font><span>I contact you about our meeting for tomorrow. Here is the schedule I propose:</span></font>",
-    "<li><span>9 AM: brainstorming about our new amazing business app</span></li>",
-    "<li><span>9.45 AM: summary</span></li>",
-    "<li><span>10 AM: meeting with Fabien to present our app</span></li>",
-    "<font><span>Is everything ok for you ?</span></font>"]
-HTML_2_OUT = []
-
-HTML_3 = """<div><pre>This is an answer.
-
-Regards,
-XXXXXX
------ Mail original -----</pre>
-
-
-<pre>Hi, 
-
-
-My CRM-related question.
-
-Regards, 
-
-XXXX</pre></div>"""
-
-HTML_3_IN = ["""<div><pre>This is an answer.
-
-Regards,
-XXXXXX
------ Mail original -----</pre>"""]
-HTML_3_OUT = ["Hi,", "My CRM-related question.",
-    "Regards,"]
-
-HTML_4 = """
-<div>
-    <div>Hi Nicholas,</div>
-    <br>
-    <div>I'm free now. 00447710085916.</div>
-    <br>
-    <div>Regards,</div>
-    <div>Nicholas</div>
-    <br>
-    <span id="OLK_SRC_BODY_SECTION">
-        <div style="font-family:Calibri; font-size:11pt; text-align:left; color:black; BORDER-BOTTOM: medium none; BORDER-LEFT: medium none; PADDING-BOTTOM: 0in; PADDING-LEFT: 0in; PADDING-RIGHT: 0in; BORDER-TOP: #b5c4df 1pt solid; BORDER-RIGHT: medium none; PADDING-TOP: 3pt">
-            <span style="font-weight:bold">From: </span>OpenERP Enterprise &lt;<a href="mailto:sales@openerp.com">sales@openerp.com</a>&gt;<br><span style="font-weight:bold">Reply-To: </span>&lt;<a href="mailto:sales@openerp.com">sales@openerp.com</a>&gt;<br><span style="font-weight:bold">Date: </span>Wed, 17 Apr 2013 13:30:47 +0000<br><span style="font-weight:bold">To: </span>Microsoft Office User &lt;<a href="mailto:n.saxlund@babydino.com">n.saxlund@babydino.com</a>&gt;<br><span style="font-weight:bold">Subject: </span>Re: your OpenERP.com registration<br>
-        </div>
-        <br>
-        <div>
-            <p>Hello Nicholas Saxlund, </p>
-            <p>I noticed you recently registered to our OpenERP Online solution. </p>
-            <p>You indicated that you wish to use OpenERP in your own company. We would like to know more about your your business needs and requirements, and see how we can help you. When would you be available to discuss your project ?
-            </p>
-            <p>Best regards, </p>
-            <pre><a href="http://openerp.com">http://openerp.com</a>
-Belgium: +32.81.81.37.00
-U.S.: +1 (650) 307-6736
-India: +91 (79) 40 500 100
-                        </pre>
-        </div>
-    </span>
-</div>"""
-
-HTML_5 = """<div><pre>Hi,
-
-I have downloaded OpenERP installer 7.0 and successfully installed the postgresql server and the OpenERP.
-I created a database and started to install module by log in as administrator.
-However, I was not able to install any module due to "OpenERP Server Error" as shown in the attachement.
-Could you please let me know how could I fix this problem?
-
-&nbsp;Regards,
-Goh Sin Yih
-
-
-________________________________
- From: OpenERP Enterprise &lt;sales@openerp.com&gt;
-To: sinyih_goh@yahoo.com 
-Sent: Friday, February 8, 2013 12:46 AM
-Subject: Feedback From Your OpenERP Trial
-
-Hello Goh Sin Yih, 
-Thank you for having tested OpenERP Online. 
-I noticed you started a trial of OpenERP Online (gsy) but you did not decide to keep using it. 
-So, I just wanted to get in touch with you to get your feedback. Can you tell me what kind of application you were you looking for and why you didn't decide to continue with OpenERP? 
-Thanks in advance for providing your feedback, 
-Do not hesitate to contact me if you have any questions, 
-Thanks, 
-</pre>"""
-
-GMAIL_1 = """Hello,<div><br></div><div>Ok for me. I am replying directly in gmail, without signature.</div><div><br></div><div>Kind regards,</div><div><br></div><div>Demo.<br><br><div>On Thu, Nov 8, 2012 at 5:29 PM,  <span>&lt;<a href="mailto:dummy@example.com">dummy@example.com</a>&gt;</span> wrote:<br><blockquote><div>I contact you about our meeting for tomorrow. Here is the schedule I propose:</div><div><ul><li>9 AM: brainstorming about our new amazing business app&lt;/span&gt;&lt;/li&gt;</li>
-<li>9.45 AM: summary</li><li>10 AM: meeting with Fabien to present our app</li></ul></div><div>Is everything ok for you ?</div>
-<div><p>--<br>Administrator</p></div>
-
-<div><p>Log in our portal at: <a href="http://localhost:8069#action=login&amp;db=mail_1&amp;login=demo">http://localhost:8069#action=login&amp;db=mail_1&amp;login=demo</a></p></div>
-</blockquote></div><br></div>"""
-
-GMAIL_1_IN = ['Ok for me. I am replying directly in gmail, without signature.']
-GMAIL_1_OUT = ['Administrator', 'Log in our portal at:']
-
-THUNDERBIRD_1 = """<div>On 11/08/2012 05:29 PM,
-      <a href="mailto:dummy@example.com">dummy@example.com</a> wrote:<br></div>
-    <blockquote>
-      <div>I contact you about our meeting for tomorrow. Here is the
-        schedule I propose:</div>
-      <div>
-        <ul><li>9 AM: brainstorming about our new amazing business
-            app&lt;/span&gt;&lt;/li&gt;</li>
-          <li>9.45 AM: summary</li>
-          <li>10 AM: meeting with Fabien to present our app</li>
-        </ul></div>
-      <div>Is everything ok for you ?</div>
-      <div>
-        <p>--<br>
-          Administrator</p>
-      </div>
-      <div>
-        <p>Log in our portal at:
-<a href="http://localhost:8069#action=login&amp;db=mail_1&amp;token=rHdWcUART5PhEnJRaXjH">http://localhost:8069#action=login&amp;db=mail_1&amp;token=rHdWcUART5PhEnJRaXjH</a></p>
-      </div>
-    </blockquote>
-    Ok for me. I am replying directly below your mail, using Thunderbird, with a signature.<br><br>
-    Did you receive my email about my new laptop, by the way ?<br><br>
-    Raoul.<br><pre>-- 
-Raoul Grosbedonn&#233;e
-</pre>"""
-
-THUNDERBIRD_1_IN = ['Ok for me. I am replying directly below your mail, using Thunderbird, with a signature.']
-THUNDERBIRD_1_OUT = ['I contact you about our meeting for tomorrow.', 'Raoul Grosbedon']
-
-HOTMAIL_1 = """<div>
-    <div dir="ltr"><br>&nbsp;
-        I have an amazing company, i'm learning OpenERP, it is a small company yet, but plannig to grow up quickly.
-        <br>&nbsp;<br>Kindest regards,<br>xxx<br>
-        <div>
-            <div id="SkyDrivePlaceholder">
-            </div>
-            <hr id="stopSpelling">
-            Subject: Re: your OpenERP.com registration<br>From: xxx@xxx.xxx<br>To: xxx@xxx.xxx<br>Date: Wed, 27 Mar 2013 17:12:12 +0000
-            <br><br>
-            Hello xxx,
-            <br>
-            I noticed you recently created an OpenERP.com account to access OpenERP Apps.
-            <br>
-            You indicated that you wish to use OpenERP in your own company.
-            We would like to know more about your your business needs and requirements, and see how
-            we can help you. When would you be available to discuss your project ?<br>
-            Best regards,<br>
-            <pre>
-                <a href="http://openerp.com" target="_blank">http://openerp.com</a>
-                Belgium: +32.81.81.37.00
-                U.S.: +1 (650) 307-6736
-                India: +91 (79) 40 500 100
-            </pre>
-        </div>
-    </div>
-</div>"""
-
-HOTMAIL_1_IN = ["I have an amazing company, i'm learning OpenERP, it is a small company yet, but plannig to grow up quickly."]
-HOTMAIL_1_OUT = ["Subject: Re: your OpenERP.com registration", " I noticed you recently created an OpenERP.com account to access OpenERP Apps.",
-    "We would like to know more about your your business needs and requirements", "Belgium: +32.81.81.37.00"]
-
-MSOFFICE_1 = """
-<div>
-<div class="WordSection1">
-        <p class="MsoNormal">
-            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-                Our requirements are simple. Just looking to replace some spreadsheets for tracking quotes and possibly using the timecard module.
-                We are a company of 25 engineers providing product design services to clients.
-            </span>
-        </p>
-        <p></p>
-        <p></p>
-        <p class="MsoNormal">
-            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-                I’ll install on a windows server and run a very limited trial to see how it works.
-                If we adopt OpenERP we will probably move to Linux or look for a hosted SaaS option.
-            </span>
-        </p>
-        <p></p>
-        <p></p>
-        <p class="MsoNormal">
-            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-                <br>
-                I am also evaluating Adempiere and maybe others.
-            </span>
-        </p>
-        <p></p>
-        <p></p>
-        <p class="MsoNormal">
-            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-            </span>
-        </p>
-        <p>&nbsp;</p>
-        <p></p>
-        <p class="MsoNormal">
-            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-                I expect the trial will take 2-3 months as this is not a high priority for us.
-            </span>
-        </p>
-        <p></p>
-        <p></p>
-        <p class="MsoNormal">
-            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-            </span>
-        </p>
-        <p>&nbsp;</p>
-        <p></p>
-        <p class="MsoNormal">
-            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-                Alan
-            </span>
-        </p>
-        <p></p>
-        <p></p>
-        <p class="MsoNormal">
-            <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-            </span>
-        </p>
-        <p>&nbsp;</p>
-        <p></p>
-        <div>
-            <div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
-                <p class="MsoNormal">
-                    <b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">
-                        From:
-                    </span></b>
-                    <span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">
-                        OpenERP Enterprise [mailto:sales@openerp.com]
-                        <br><b>Sent:</b> Monday, 11 March, 2013 14:47<br><b>To:</b> Alan Widmer<br><b>Subject:</b> Re: your OpenERP.com registration
-                    </span>
-                </p>
-                <p></p>
-                <p></p>
-            </div>
-        </div>
-        <p class="MsoNormal"></p>
-        <p>&nbsp;</p>
-        <p>Hello Alan Widmer, </p>
-        <p></p>
-        <p>I noticed you recently downloaded OpenERP. </p>
-        <p></p>
-        <p>
-            Uou mentioned you wish to use OpenERP in your own company. Please let me more about your
-            business needs and requirements? When will you be available to discuss about your project?
-        </p>
-        <p></p>
-        <p>Thanks for your interest in OpenERP, </p>
-        <p></p>
-        <p>Feel free to contact me if you have any questions, </p>
-        <p></p>
-        <p>Looking forward to hear from you soon. </p>
-        <p></p>
-        <pre><p>&nbsp;</p></pre>
-        <pre>--<p></p></pre>
-        <pre>Nicolas<p></p></pre>
-        <pre><a href="http://openerp.com">http://openerp.com</a><p></p></pre>
-        <pre>Belgium: +32.81.81.37.00<p></p></pre>
-        <pre>U.S.: +1 (650) 307-6736<p></p></pre>
-        <pre>India: +91 (79) 40 500 100<p></p></pre>
-        <pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<p></p></pre>
-    </div>
-</div>"""
-
-MSOFFICE_1_IN = ['Our requirements are simple. Just looking to replace some spreadsheets for tracking quotes and possibly using the timecard module.']
-MSOFFICE_1_OUT = ['I noticed you recently downloaded OpenERP.', 'Uou mentioned you wish to use OpenERP in your own company.', 'Belgium: +32.81.81.37.00']
-
-MSOFFICE_2 = """
-<div>
-  <div class="WordSection1">
-    <p class="MsoNormal">
-      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Nicolas,</span></p><p></p>
-    <p></p>
-    <p class="MsoNormal" style="text-indent:.5in">
-      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">We are currently investigating the possibility of moving away from our current ERP </span></p><p></p>
-    <p></p>
-    <p class="MsoNormal">
-      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-        </span></p><p>&nbsp;</p>
-      
-    <p></p>
-    <p class="MsoNormal">
-      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Thank You</span></p><p></p>
-    <p></p>
-    <p class="MsoNormal">
-      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Matt</span></p><p></p>
-    <p></p>
-    <p class="MsoNormal">
-      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-        </span></p><p>&nbsp;</p>
-      
-    <p></p>
-    <div>
-      <p class="MsoNormal">
-        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Raoul Petitpoil</span></p><p></p>
-      <p></p>
-      <p class="MsoNormal">
-        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Poil Industries</span></p><p></p>
-      <p></p>
-      <p class="MsoNormal">
-        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Information Technology</span></p><p></p>
-      <p></p>
-      <p class="MsoNormal">
-        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">920 Super Street</span></p><p></p>
-      <p></p>
-      <p class="MsoNormal">
-        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Sanchez, Pa 17046 USA</span></p><p></p>
-      <p></p>
-      <p class="MsoNormal">
-        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Tel: xxx.xxx</span></p><p></p>
-      <p></p>
-      <p class="MsoNormal">
-        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Fax: xxx.xxx</span></p><p></p>
-      <p></p>
-      <p class="MsoNormal">
-        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Email: </span>
-        <a href="mailto:raoul@petitpoil.com">
-          <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:blue">raoul@petitpoil.com</span>
-        </a>
-        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-          </span></p><p></p>
-        
-      <p></p>
-      <p class="MsoNormal">
-        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">www.poilindustries.com</span></p><p></p>
-      <p></p>
-      <p class="MsoNormal">
-        <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">www.superproducts.com</span></p><p></p>
-      <p></p>
-    </div>
-    <p class="MsoNormal">
-      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-        </span></p><p>&nbsp;</p>
-      
-    <p></p>
-    <div>
-      <div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
-        <p class="MsoNormal">
-          <b>
-            <span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">From:</span>
-          </b>
-          <span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;"> OpenERP Enterprise [mailto:sales@openerp.com] <br><b>Sent:</b> Wednesday, April 17, 2013 1:31 PM<br><b>To:</b> Matt Witters<br><b>Subject:</b> Re: your OpenERP.com registration</span></p><p></p>
-        <p></p>
-      </div>
-    </div>
-    <p class="MsoNormal"></p>
-    <p>&nbsp;</p>
-    <p>Hello Raoul Petitpoil, </p>
-    <p></p>
-    <p>I noticed you recently downloaded OpenERP. </p>
-    <p></p>
-    <p>You indicated that you wish to use OpenERP in your own company. We would like to know more about your your business needs and requirements, and see how we can help you. When would you be available to discuss your project ? </p>
-    <p></p>
-    <p>Best regards, </p>
-    <p></p>
-    <pre>      <p>&nbsp;</p>
-    </pre>
-    <pre>--<p></p></pre>
-    <pre>Nicolas<p></p></pre>
-    <pre>      <a href="http://openerp.com">http://openerp.com</a>
-      <p></p>
-    </pre>
-    <pre>Belgium: +32.81.81.37.00<p></p></pre>
-    <pre>U.S.: +1 (650) 307-6736<p></p></pre>
-    <pre>India: +91 (79) 40 500 100<p></p></pre>
-    <pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <p></p></pre>
-  </div>
-</div>"""
-
-MSOFFICE_2_IN = ['We are currently investigating the possibility']
-MSOFFICE_2_OUT = ['I noticed you recently downloaded OpenERP.', 'You indicated that you wish', 'Belgium: +32.81.81.37.00']
-
-MSOFFICE_3 = """<div>
-  <div class="WordSection1">
-    <p class="MsoNormal">
-      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Hi Nicolas&nbsp;!</span></p><p></p>
-    <p></p>
-    <p class="MsoNormal">
-      <span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-        </span></p><p>&nbsp;</p>
-      
-    <p></p>
-    <p class="MsoNormal">
-      <span lang="EN-US" style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Yes I’d be glad to hear about your offers as we struggle every year with the planning/approving of LOA. </span></p><p></p>
-    <p></p>
-    <p class="MsoNormal">
-      <span lang="EN-US" style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">I saw your boss yesterday on tv and immediately wanted to test the interface. </span></p><p></p>
-    <p></p>
-    <p class="MsoNormal">
-      <span lang="EN-US" style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-        </span></p><p>&nbsp;</p>
-
-    <p></p>
-    <div>
-      <p class="MsoNormal">
-        <b>
-          <span lang="NL-BE" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">Bien à vous, </span></b></p><p></p><b>
-        </b>
-      <p></p>
-      <p class="MsoNormal">
-        <b>
-          <span lang="NL-BE" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">Met vriendelijke groeten, </span></b></p><p></p><b>
-        </b>
-      <p></p>
-      <p class="MsoNormal">
-        <b>
-          <span lang="EN-GB" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">Best regards,</span></b></p><p></p><b>
-        </b>
-      <p></p>
-      <p class="MsoNormal">
-        <b>
-          <span lang="EN-GB" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">
-            </span></b></p><p><b>&nbsp;</b></p><b>
-          
-        </b>
-      <p></p>
-      <p class="MsoNormal">
-        <b>
-          <span lang="EN-GB" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">R. Petitpoil&nbsp;&nbsp;&nbsp; <br></span>
-        </b>
-        <span lang="EN-GB" style="font-size:10.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">Human Resource Manager<b><br><br>Field Resource s.a n.v.&nbsp;&nbsp;<i> <br></i></b>Hermesstraat 6A <br>1930 Zaventem</span>
-        <span lang="EN-GB" style="font-size:8.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;;color:gray"><br></span>
-        <b>
-          <span lang="FR" style="font-size:10.0pt;font-family:Wingdings;color:#1F497D">(</span>
-        </b>
-        <b>
-          <span lang="FR" style="font-size:9.0pt;font-family:Wingdings;color:#1F497D"> </span>
-        </b>
-        <b>
-          <span lang="EN-GB" style="font-size:8.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">xxx.xxx &nbsp;</span>
-        </b>
-        <b>
-          <span lang="EN-GB" style="font-size:9.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray"><br></span>
-        </b>
-        <b>
-          <span lang="FR" style="font-size:10.0pt;font-family:&quot;Wingdings 2&quot;;color:#1F497D">7</span>
-        </b>
-        <b>
-          <span lang="FR" style="font-size:9.0pt;font-family:&quot;Wingdings 2&quot;;color:#1F497D"> </span>
-        </b>
-        <b>
-          <span lang="EN-GB" style="font-size:8.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:gray">+32 2 727.05.91<br></span>
-        </b>
-        <span lang="EN-GB" style="font-size:24.0pt;font-family:Webdings;color:green">P</span>
-        <span lang="EN-GB" style="font-size:8.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;;color:green"> <b>&nbsp;&nbsp; </b></span>
-        <b>
-          <span lang="EN-GB" style="font-size:9.0pt;font-family:&quot;Trebuchet MS&quot;,&quot;sans-serif&quot;;color:green">Please consider the environment before printing this email.</span>
-        </b>
-        <span lang="EN-GB" style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:navy"> </span>
-        <span lang="EN-GB" style="font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:navy">
-          </span></p><p></p>
-        
-      <p></p>
-    </div>
-    <p class="MsoNormal">
-      <span lang="EN-US" style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">
-        </span></p><p>&nbsp;</p>
-      
-    <p></p>
-    <div>
-      <div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm">
-        <p class="MsoNormal">
-          <b>
-            <span lang="FR" style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">De&nbsp;:</span>
-          </b>
-          <span lang="FR" style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;"> OpenERP Enterprise [mailto:sales@openerp.com] <br><b>Envoyé&nbsp;:</b> jeudi 18 avril 2013 11:31<br><b>À&nbsp;:</b> Paul Richard<br><b>Objet&nbsp;:</b> Re: your OpenERP.com registration</span></p><p></p>
-        <p></p>
-      </div>
-    </div>
-    <p class="MsoNormal"></p>
-    <p>&nbsp;</p>
-    <p>Hello Raoul PETITPOIL, </p>
-    <p></p>
-    <p>I noticed you recently registered to our OpenERP Online solution. </p>
-    <p></p>
-    <p>You indicated that you wish to use OpenERP in your own company. We would like to know more about your your business needs and requirements, and see how we can help you. When would you be available to discuss your project ? </p>
-    <p></p>
-    <p>Best regards, </p>
-    <p></p>
-    <pre>      <p>&nbsp;</p>
-    </pre>
-    <pre>--<p></p></pre>
-    <pre>Nicolas<p></p></pre>
-    <pre>      <a href="http://openerp.com">http://openerp.com</a>
-      <p></p>
-    </pre>
-    <pre>Belgium: +32.81.81.37.00<p></p></pre>
-    <pre>U.S.: +1 (650) 307-6736<p></p></pre>
-    <pre>India: +91 (79) 40 500 100<p></p></pre>
-    <pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <p></p></pre>
-  </div>
-</div>"""
-
-MSOFFICE_3_IN = ['I saw your boss yesterday']
-MSOFFICE_3_OUT = ['I noticed you recently downloaded OpenERP.', 'You indicated that you wish', 'Belgium: +32.81.81.37.00']
-
-
-# ------------------------------------------------------------
-# Test cases coming from bugs
-# ------------------------------------------------------------
-
-# bug: read more not apparent, strange message in read more span
-BUG1 = """<pre>Hi Migration Team,
-
-Paragraph 1, blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah.
-
-Paragraph 2, blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah.
-
-Paragraph 3, blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah blah blah blah blah blah blah 
-blah blah blah blah blah blah blah blah.
-
-Thanks.
-
-Regards,
-
--- 
-Olivier Laurent
-Migration Manager
-OpenERP SA
-Chaussée de Namur, 40
-B-1367 Gérompont
-Tel: +32.81.81.37.00
-Web: http://www.openerp.com</pre>"""
-
-BUG_1_IN = [
-    'Hi Migration Team',
-    'Paragraph 1'
-]
-BUG_1_OUT = [
-    'Olivier Laurent',
-    'Chaussée de Namur',
-    '81.81.37.00',
-    'openerp.com',
-]
-
-
-BUG2 = """
-<div>
-    <br>
-    <div class="moz-forward-container"><br>
-      <br>
-      -------- Original Message --------
-      <table class="moz-email-headers-table" border="0" cellpadding="0" cellspacing="0">
-        <tbody>
-          <tr>
-            <th nowrap="" valign="BASELINE" align="RIGHT">Subject:
-            </th>
-            <td>Fwd: TR: OpenERP S.A. Payment Reminder</td>
-          </tr>
-          <tr>
-            <th nowrap="" valign="BASELINE" align="RIGHT">Date: </th>
-            <td>Wed, 16 Oct 2013 14:11:13 +0200</td>
-          </tr>
-          <tr>
-            <th nowrap="" valign="BASELINE" align="RIGHT">From: </th>
-            <td>Christine Herrmann <a class="moz-txt-link-rfc2396E" href="mailto:che@openerp.com">&lt;che@openerp.com&gt;</a></td>
-          </tr>
-          <tr>
-            <th nowrap="" valign="BASELINE" align="RIGHT">To: </th>
-            <td><a class="moz-txt-link-abbreviated" href="mailto:online@openerp.com">online@openerp.com</a></td>
-          </tr>
-        </tbody>
-      </table>
-      <br>
-      <br>
-      
-      <br>
-      <div class="moz-forward-container"><br>
-        <br>
-        -------- Message original --------
-        <table class="moz-email-headers-table" border="0" cellpadding="0" cellspacing="0">
-          <tbody>
-            <tr>
-              <th nowrap="" valign="BASELINE" align="RIGHT">Sujet:
-              </th>
-              <td>TR: OpenERP S.A. Payment Reminder</td>
-            </tr>
-            <tr>
-              <th nowrap="" valign="BASELINE" align="RIGHT">Date&nbsp;:
-              </th>
-              <td>Wed, 16 Oct 2013 10:34:45 -0000</td>
-            </tr>
-            <tr>
-              <th nowrap="" valign="BASELINE" align="RIGHT">De&nbsp;: </th>
-              <td>Ida Siwatala <a class="moz-txt-link-rfc2396E" href="mailto:infos@inzoservices.com">&lt;infos@inzoservices.com&gt;</a></td>
-            </tr>
-            <tr>
-              <th nowrap="" valign="BASELINE" align="RIGHT">Répondre
-
-                à&nbsp;: </th>
-              <td><a class="moz-txt-link-abbreviated" href="mailto:catchall@openerp.my.openerp.com">catchall@openerp.my.openerp.com</a></td>
-            </tr>
-            <tr>
-              <th nowrap="" valign="BASELINE" align="RIGHT">Pour&nbsp;:
-              </th>
-              <td>Christine Herrmann (che) <a class="moz-txt-link-rfc2396E" href="mailto:che@openerp.com">&lt;che@openerp.com&gt;</a></td>
-            </tr>
-          </tbody>
-        </table>
-        <br>
-        <br>
-        <div>
-          <div class="WordSection1">
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Bonjour,</span></p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
-            <p>&nbsp;</p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Pourriez-vous
-
-                me faire un retour sur ce point.</span></p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
-            <p>&nbsp;</p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Cordialement</span></p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
-            <p>&nbsp;</p>
-            <div>
-              <div style="border:none;border-top:solid #B5C4DF
-                1.0pt;padding:3.0pt 0cm 0cm 0cm">
-                <p class="MsoNormal"><b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">De&nbsp;:</span></b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">
-                    Ida Siwatala [<a class="moz-txt-link-freetext" href="mailto:infos@inzoservices.com">mailto:infos@inzoservices.com</a>]
-                    <br>
-                    <b>Envoyé&nbsp;:</b> vendredi 4 octobre 2013 20:03<br>
-                    <b>À&nbsp;:</b> 'Followers of
-                    INZO-services-8-all-e-Maxime-Lisbonne-77176-Savigny-le-temple-France'<br>
-                    <b>Objet&nbsp;:</b> RE: OpenERP S.A. Payment Reminder</span></p>
-              </div>
-            </div>
-            <p>&nbsp;</p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Bonsoir,</span></p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
-            <p>&nbsp;</p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Je
-
-                me permets de revenir vers vous par écrit , car j’ai
-                fait 2 appels vers votre service en exposant mon
-                problème, mais je n’ai pas eu de retour.</span></p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Cela
-
-                fait un mois que j’ai fait la souscription de votre
-                produit, mais je me rends compte qu’il est pas adapté à
-                ma situation ( fonctionnalité manquante et surtout je
-                n’ai pas beaucoup de temps à passer à résoudre des
-                bugs). </span></p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">C’est
-
-                pourquoi , j’ai demandé qu’un accord soit trouvé avec
-                vous pour annuler le contrat (tout en vous payant le
-                mois d’utilisation de septembre).</span></p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
-            <p>&nbsp;</p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Pourriez-vous
-
-                me faire un retour sur ce point.</span></p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
-            <p>&nbsp;</p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Cordialement,</span></p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
-            <p>&nbsp;</p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Ida
-
-                Siwatala</span></p>
-            <p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"></span></p>
-            <p>&nbsp;</p>
-            <p class="MsoNormal"><b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">De&nbsp;:</span></b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">
-                <a href="mailto:che@openerp.com">che@openerp.com</a>
-                [<a href="mailto:che@openerp.com">mailto:che@openerp.com</a>]
-                <br>
-                <b>Envoyé&nbsp;:</b> vendredi 4 octobre 2013 17:41<br>
-                <b>À&nbsp;:</b> <a href="mailto:infos@inzoservices.com">infos@inzoservices.com</a><br>
-                <b>Objet&nbsp;:</b> OpenERP S.A. Payment Reminder</span></p>
-            <p>&nbsp;</p>
-            <div>
-              <p style="background:white"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222">Dear
-
-                  INZO services,</span></p>
-              <p style="background:white"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222">Exception
-
-                  made if there was a mistake of ours, it seems that the
-                  following amount stays unpaid. Please, take
-                  appropriate measures in order to carry out this
-                  payment in the next 8 days. </span></p>
-              <p class="MsoNormal" style="background:white"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222"></span></p>
-              <p>&nbsp;</p>
-              <table class="MsoNormalTable" style="width:100.0%;border:outset 1.5pt" width="100%" border="1" cellpadding="0">
-                <tbody>
-                  <tr>
-                    <td style="padding:.75pt .75pt .75pt .75pt">
-                      <p class="MsoNormal">Date de facturation</p>
-                    </td>
-                    <td style="padding:.75pt .75pt .75pt .75pt">
-                      <p class="MsoNormal">Description</p>
-                    </td>
-                    <td style="padding:.75pt .75pt .75pt .75pt">
-                      <p class="MsoNormal">Reference</p>
-                    </td>
-                    <td style="padding:.75pt .75pt .75pt .75pt">
-                      <p class="MsoNormal">Due Date</p>
-                    </td>
-                    <td style="padding:.75pt .75pt .75pt .75pt">
-                      <p class="MsoNormal">Amount (€)</p>
-                    </td>
-                    <td style="padding:.75pt .75pt .75pt .75pt">
-                      <p class="MsoNormal">Lit.</p>
-                    </td>
-                  </tr>
-                  <tr>
-                    <td style="padding:.75pt .75pt .75pt .75pt">
-                      <p class="MsoNormal"><b>2013-09-24</b></p>
-                    </td>
-                    <td style="padding:.75pt .75pt .75pt .75pt">
-                      <p class="MsoNormal"><b>2013/1121</b></p>
-                    </td>
-                    <td style="padding:.75pt .75pt .75pt .75pt">
-                      <p class="MsoNormal"><b>Enterprise - Inzo Services
-                          - Juillet 2013</b></p>
-                    </td>
-                    <td style="padding:.75pt .75pt .75pt .75pt">
-                      <p class="MsoNormal"><b>2013-09-24</b></p>
-                    </td>
-                    <td style="padding:.75pt .75pt .75pt .75pt">
-                      <p class="MsoNormal"><b>420.0</b></p>
-                    </td>
-                    <td style="padding:.75pt .75pt .75pt .75pt"><br>
-                    </td>
-                  </tr>
-                  <tr>
-                    <td style="padding:.75pt .75pt .75pt .75pt"><br>
-                    </td>
-                    <td style="border:none;padding:.75pt .75pt .75pt
-                      .75pt"><br>
-                    </td>
-                    <td style="border:none;padding:.75pt .75pt .75pt
-                      .75pt"><br>
-                    </td>
-                    <td style="border:none;padding:.75pt .75pt .75pt
-                      .75pt"><br>
-                    </td>
-                    <td style="border:none;padding:.75pt .75pt .75pt
-                      .75pt"><br>
-                    </td>
-                    <td style="border:none;padding:.75pt .75pt .75pt
-                      .75pt"><br>
-                    </td>
-                  </tr>
-                </tbody>
-              </table>
-              <p class="MsoNormal" style="text-align:center;background:white" align="center"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222">Amount
-
-                  due : 420.00 € </span></p>
-              <p style="background:white"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222">Would
-
-                  your payment have been carried out after this mail was
-                  sent, please ignore this message. Do not hesitate to
-                  contact our accounting department. </span></p>
-              <p class="MsoNormal" style="background:white"><span style="font-size:9.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;;color:#222222"><br>
-                  Best Regards, <br>
-                  Aurore Lesage <br>
-                  OpenERP<br>
-                  Chaussée de Namur, 40 <br>
-                  B-1367 Grand Rosières <br>
-                  Tel: +32.81.81.37.00 - Fax: +32.81.73.35.01 <br>
-                  E-mail : <a href="mailto:ale@openerp.com">ale@openerp.com</a> <br>
-                  Web: <a href="http://www.openerp.com">http://www.openerp.com</a></span></p>
-            </div>
-          </div>
-        </div>
-        --<br>
-        INZO services <small>Sent by <a style="color:inherit" href="http://www.openerp.com">OpenERP
-            S.A.</a> using <a style="color:inherit" href="https://www.openerp.com/">OpenERP</a>.</small>
-        <small>Access your messages and documents <a style="color:inherit" href="https://accounts.openerp.com?db=openerp#action=mail.action_mail_redirect&amp;login=che&amp;message_id=5750830">in
-
-            OpenERP</a></small> <br>
-        <pre class="moz-signature" cols="72">-- 
-Christine Herrmann 
-
-OpenERP 
-Chaussée de Namur, 40 
-B-1367 Grand Rosières 
-Tel: +32.81.81.37.00 - Fax: +32.81.73.35.01 
-
-Web: <a class="moz-txt-link-freetext" href="http://www.openerp.com">http://www.openerp.com</a> </pre>
-        <br>
-      </div>
-      <br>
-      <br>
-    </div>
-    <br>
-  
-</div>"""
-
-BUG_2_IN = [
-    'read more',
-    '...',
-]
-BUG_2_OUT = [
-    'Fwd: TR: OpenERP S.A'
-    'fait un mois'
-]
diff --git a/openerp/tests/test_misc.py b/openerp/tests/test_misc.py
deleted file mode 100644 (file)
index 865b51d..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-# This test can be run stand-alone with something like:
-# > PYTHONPATH=. python2 openerp/tests/test_misc.py
-import datetime
-import locale
-import unittest2
-
-import babel
-import babel.dates
-
-from ..tools import misc
-
-
-class test_countingstream(unittest2.TestCase):
-    def test_empty_stream(self):
-        s = misc.CountingStream(iter([]))
-        self.assertEqual(s.index, -1)
-        self.assertIsNone(next(s, None))
-        self.assertEqual(s.index, 0)
-
-    def test_single(self):
-        s = misc.CountingStream(xrange(1))
-        self.assertEqual(s.index, -1)
-        self.assertEqual(next(s, None), 0)
-        self.assertIsNone(next(s, None))
-        self.assertEqual(s.index, 1)
-
-    def test_full(self):
-        s = misc.CountingStream(xrange(42))
-        for _ in s:
-            pass
-        self.assertEqual(s.index, 42)
-
-    def test_repeated(self):
-        """ Once the CountingStream has stopped iterating, the index should not
-        increase anymore (the internal state should not be allowed to change)
-        """
-        s = misc.CountingStream(iter([]))
-        self.assertIsNone(next(s, None))
-        self.assertEqual(s.index, 0)
-        self.assertIsNone(next(s, None))
-        self.assertEqual(s.index, 0)
-
-if __name__ == '__main__':
-    unittest2.main()
diff --git a/openerp/tests/test_orm.py b/openerp/tests/test_orm.py
deleted file mode 100644 (file)
index b85a0e2..0000000
+++ /dev/null
@@ -1,382 +0,0 @@
-from collections import defaultdict
-from openerp.tools import mute_logger
-import common
-
-UID = common.ADMIN_USER_ID
-DB = common.DB
-
-
-class TestORM(common.TransactionCase):
-    """ test special behaviors of ORM CRUD functions
-    
-        TODO: use real Exceptions types instead of Exception """
-
-    def setUp(self):
-        super(TestORM, self).setUp()
-        cr, uid = self.cr, self.uid
-        self.partner = self.registry('res.partner')
-        self.users = self.registry('res.users')
-        self.p1 = self.partner.name_create(cr, uid, 'W')[0]
-        self.p2 = self.partner.name_create(cr, uid, 'Y')[0]
-        self.ir_rule = self.registry('ir.rule')
-
-        # sample unprivileged user
-        employee_gid = self.ref('base.group_user')
-        self.uid2 = self.users.create(cr, uid, {'name': 'test user', 'login': 'test', 'groups_id': [4,employee_gid]})
-
-    @mute_logger('openerp.osv.orm')
-    def testAccessDeletedRecords(self):
-        """ Verify that accessing deleted records works as expected """
-        cr, uid, uid2, p1, p2 = self.cr, self.uid, self.uid2, self.p1, self.p2
-        self.partner.unlink(cr, uid, [p1])
-
-        # read() is expected to skip deleted records because our API is not
-        # transactional for a sequence of search()->read() performed from the
-        # client-side... a concurrent deletion could therefore cause spurious
-        # exceptions even when simply opening a list view!
-        # /!\ Using unprileged user to detect former side effects of ir.rules!
-        self.assertEqual([{'id': p2, 'name': 'Y'}], self.partner.read(cr, uid2, [p1,p2], ['name']), "read() should skip deleted records")
-        self.assertEqual([], self.partner.read(cr, uid2, [p1], ['name']), "read() should skip deleted records")
-
-        # Deleting an already deleted record should be simply ignored
-        self.assertTrue(self.partner.unlink(cr, uid, [p1]), "Re-deleting should be a no-op")
-
-        # Updating an already deleted record should raise, even as admin
-        with self.assertRaises(Exception):
-            self.partner.write(cr, uid, [p1], {'name': 'foo'})
-
-    @mute_logger('openerp.osv.orm')
-    def testAccessFilteredRecords(self):
-        """ Verify that accessing filtered records works as expected for non-admin user """
-        cr, uid, uid2, p1, p2 = self.cr, self.uid, self.uid2, self.p1, self.p2
-        partner_model = self.registry('ir.model').search(cr, uid, [('model','=','res.partner')])[0]
-        self.ir_rule.create(cr, uid, {'name': 'Y is invisible',
-                                      'domain_force': [('id', '!=', p1)],
-                                      'model_id': partner_model})
-        # search as unprivileged user
-        partners = self.partner.search(cr, uid2, [])
-        self.assertFalse(p1 in partners, "W should not be visible...")
-        self.assertTrue(p2 in partners, "... but Y should be visible")
-
-        # read as unprivileged user
-        with self.assertRaises(Exception):
-            self.partner.read(cr, uid2, [p1], ['name'])
-        # write as unprivileged user
-        with self.assertRaises(Exception):
-            self.partner.write(cr, uid2, [p1], {'name': 'foo'})
-        # unlink as unprivileged user
-        with self.assertRaises(Exception):
-            self.partner.unlink(cr, uid2, [p1])
-
-        # Prepare mixed case 
-        self.partner.unlink(cr, uid, [p2])
-        # read mixed records: some deleted and some filtered
-        with self.assertRaises(Exception):
-            self.partner.read(cr, uid2, [p1,p2], ['name'])
-        # delete mixed records: some deleted and some filtered
-        with self.assertRaises(Exception):
-            self.partner.unlink(cr, uid2, [p1,p2])
-
-    @mute_logger('openerp.osv.orm')
-    def test_search_read(self):
-        # simple search_read
-        self.partner.create(self.cr, UID, {'name': 'MyPartner1'})
-        found = self.partner.search_read(self.cr, UID, [['name', '=', 'MyPartner1']], ['name'])
-        self.assertEqual(len(found), 1)
-        self.assertEqual(found[0]['name'], 'MyPartner1')
-        self.assertTrue('id' in found[0])
-
-        # search_read correct order
-        self.partner.create(self.cr, UID, {'name': 'MyPartner2'})
-        found = self.partner.search_read(self.cr, UID, [['name', 'like', 'MyPartner']], ['name'], order="name")
-        self.assertEqual(len(found), 2)
-        self.assertEqual(found[0]['name'], 'MyPartner1')
-        self.assertEqual(found[1]['name'], 'MyPartner2')
-        found = self.partner.search_read(self.cr, UID, [['name', 'like', 'MyPartner']], ['name'], order="name desc")
-        self.assertEqual(len(found), 2)
-        self.assertEqual(found[0]['name'], 'MyPartner2')
-        self.assertEqual(found[1]['name'], 'MyPartner1')
-
-        # search_read that finds nothing
-        found = self.partner.search_read(self.cr, UID, [['name', '=', 'Does not exists']], ['name'])
-        self.assertEqual(len(found), 0)
-
-    def test_groupby_date(self):
-        partners = dict(
-            A='2012-11-19',
-            B='2012-12-17',
-            C='2012-12-31',
-            D='2013-01-07',
-            E='2013-01-14',
-            F='2013-01-28',
-            G='2013-02-11',
-        )
-
-        all_partners = []
-        partners_by_day = defaultdict(set)
-        partners_by_month = defaultdict(set)
-        partners_by_year = defaultdict(set)
-
-        for name, date in partners.items():
-            p = self.partner.create(self.cr, UID, dict(name=name, date=date))
-            all_partners.append(p)
-            partners_by_day[date].add(p)
-            partners_by_month[date.rsplit('-', 1)[0]].add(p)
-            partners_by_year[date.split('-', 1)[0]].add(p)
-
-        def read_group(interval, domain=None):
-            main_domain = [('id', 'in', all_partners)]
-            if domain:
-                domain = ['&'] + main_domain + domain
-            else:
-                domain = main_domain
-
-            rg = self.partner.read_group(self.cr, self.uid, domain, ['date'], 'date' + ':' + interval)
-            result = {}
-            for r in rg:
-                result[r['date']] = set(self.partner.search(self.cr, self.uid, r['__domain']))
-            return result
-
-        self.assertEqual(len(read_group('day')), len(partners_by_day))
-        self.assertEqual(len(read_group('month')), len(partners_by_month))
-        self.assertEqual(len(read_group('year')), len(partners_by_year))
-
-
-class TestInherits(common.TransactionCase):
-    """ test the behavior of the orm for models that use _inherits;
-        specifically: res.users, that inherits from res.partner
-    """
-
-    def setUp(self):
-        super(TestInherits, self).setUp()
-        self.partner = self.registry('res.partner')
-        self.user = self.registry('res.users')
-
-    def test_create(self):
-        """ creating a user should automatically create a new partner """
-        partners_before = self.partner.search(self.cr, UID, [])
-        foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
-        foo = self.user.browse(self.cr, UID, foo_id)
-
-        self.assertNotIn(foo.partner_id.id, partners_before)
-
-    def test_create_with_ancestor(self):
-        """ creating a user with a specific 'partner_id' should not create a new partner """
-        par_id = self.partner.create(self.cr, UID, {'name': 'Foo'})
-        partners_before = self.partner.search(self.cr, UID, [])
-        foo_id = self.user.create(self.cr, UID, {'partner_id': par_id, 'login': 'foo', 'password': 'foo'})
-        partners_after = self.partner.search(self.cr, UID, [])
-
-        self.assertEqual(set(partners_before), set(partners_after))
-
-        foo = self.user.browse(self.cr, UID, foo_id)
-        self.assertEqual(foo.name, 'Foo')
-        self.assertEqual(foo.partner_id.id, par_id)
-
-    @mute_logger('openerp.osv.orm')
-    def test_read(self):
-        """ inherited fields should be read without any indirection """
-        foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
-        foo_values, = self.user.read(self.cr, UID, [foo_id])
-        partner_id = foo_values['partner_id'][0]
-        partner_values, = self.partner.read(self.cr, UID, [partner_id])
-        self.assertEqual(foo_values['name'], partner_values['name'])
-
-        foo = self.user.browse(self.cr, UID, foo_id)
-        self.assertEqual(foo.name, foo.partner_id.name)
-
-    @mute_logger('openerp.osv.orm')
-    def test_copy(self):
-        """ copying a user should automatically copy its partner, too """
-        foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
-        foo_before, = self.user.read(self.cr, UID, [foo_id])
-        bar_id = self.user.copy(self.cr, UID, foo_id, {'login': 'bar', 'password': 'bar'})
-        foo_after, = self.user.read(self.cr, UID, [foo_id])
-
-        self.assertEqual(foo_before, foo_after)
-
-        foo, bar = self.user.browse(self.cr, UID, [foo_id, bar_id])
-        self.assertEqual(bar.login, 'bar')
-        self.assertNotEqual(foo.id, bar.id)
-        self.assertNotEqual(foo.partner_id.id, bar.partner_id.id)
-
-    @mute_logger('openerp.osv.orm')
-    def test_copy_with_ancestor(self):
-        """ copying a user with 'parent_id' in defaults should not duplicate the partner """
-        foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
-        par_id = self.partner.create(self.cr, UID, {'name': 'Bar'})
-
-        foo_before, = self.user.read(self.cr, UID, [foo_id])
-        partners_before = self.partner.search(self.cr, UID, [])
-        bar_id = self.user.copy(self.cr, UID, foo_id, {'partner_id': par_id, 'login': 'bar'})
-        foo_after, = self.user.read(self.cr, UID, [foo_id])
-        partners_after = self.partner.search(self.cr, UID, [])
-
-        self.assertEqual(foo_before, foo_after)
-        self.assertEqual(set(partners_before), set(partners_after))
-
-        foo, bar = self.user.browse(self.cr, UID, [foo_id, bar_id])
-        self.assertNotEqual(foo.id, bar.id)
-        self.assertEqual(bar.partner_id.id, par_id)
-        self.assertEqual(bar.login, 'bar', "login is given from copy parameters")
-        self.assertEqual(bar.password, foo.password, "password is given from original record")
-        self.assertEqual(bar.name, 'Bar', "name is given from specific partner")
-
-
-
-CREATE = lambda values: (0, False, values)
-UPDATE = lambda id, values: (1, id, values)
-DELETE = lambda id: (2, id, False)
-FORGET = lambda id: (3, id, False)
-LINK_TO = lambda id: (4, id, False)
-DELETE_ALL = lambda: (5, False, False)
-REPLACE_WITH = lambda ids: (6, False, ids)
-
-def sorted_by_id(list_of_dicts):
-    "sort dictionaries by their 'id' field; useful for comparisons"
-    return sorted(list_of_dicts, key=lambda d: d.get('id'))
-
-class TestO2MSerialization(common.TransactionCase):
-    """ test the orm method 'write' on one2many fields """
-
-    def setUp(self):
-        super(TestO2MSerialization, self).setUp()
-        self.partner = self.registry('res.partner')
-
-    def test_no_command(self):
-        " empty list of commands yields an empty list of records "
-        results = self.partner.resolve_2many_commands(
-            self.cr, UID, 'child_ids', [])
-
-        self.assertEqual(results, [])
-
-    def test_CREATE_commands(self):
-        " returns the VALUES dict as-is "
-        values = [{'foo': 'bar'}, {'foo': 'baz'}, {'foo': 'baq'}]
-        results = self.partner.resolve_2many_commands(
-            self.cr, UID, 'child_ids', map(CREATE, values))
-
-        self.assertEqual(results, values)
-
-    def test_LINK_TO_command(self):
-        " reads the records from the database, records are returned with their ids. "
-        ids = [
-            self.partner.create(self.cr, UID, {'name': 'foo'}),
-            self.partner.create(self.cr, UID, {'name': 'bar'}),
-            self.partner.create(self.cr, UID, {'name': 'baz'})
-        ]
-        commands = map(LINK_TO, ids)
-
-        results = self.partner.resolve_2many_commands(
-            self.cr, UID, 'child_ids', commands, ['name'])
-
-        self.assertEqual(sorted_by_id(results), sorted_by_id([
-            {'id': ids[0], 'name': 'foo'},
-            {'id': ids[1], 'name': 'bar'},
-            {'id': ids[2], 'name': 'baz'}
-        ]))
-
-    def test_bare_ids_command(self):
-        " same as the equivalent LINK_TO commands "
-        ids = [
-            self.partner.create(self.cr, UID, {'name': 'foo'}),
-            self.partner.create(self.cr, UID, {'name': 'bar'}),
-            self.partner.create(self.cr, UID, {'name': 'baz'})
-        ]
-
-        results = self.partner.resolve_2many_commands(
-            self.cr, UID, 'child_ids', ids, ['name'])
-
-        self.assertEqual(sorted_by_id(results), sorted_by_id([
-            {'id': ids[0], 'name': 'foo'},
-            {'id': ids[1], 'name': 'bar'},
-            {'id': ids[2], 'name': 'baz'}
-        ]))
-
-    def test_UPDATE_command(self):
-        " take the in-db records and merge the provided information in "
-        id_foo = self.partner.create(self.cr, UID, {'name': 'foo'})
-        id_bar = self.partner.create(self.cr, UID, {'name': 'bar'})
-        id_baz = self.partner.create(self.cr, UID, {'name': 'baz', 'city': 'tag'})
-
-        results = self.partner.resolve_2many_commands(
-            self.cr, UID, 'child_ids', [
-                LINK_TO(id_foo),
-                UPDATE(id_bar, {'name': 'qux', 'city': 'tagtag'}),
-                UPDATE(id_baz, {'name': 'quux'})
-            ], ['name', 'city'])
-
-        self.assertEqual(sorted_by_id(results), sorted_by_id([
-            {'id': id_foo, 'name': 'foo', 'city': False},
-            {'id': id_bar, 'name': 'qux', 'city': 'tagtag'},
-            {'id': id_baz, 'name': 'quux', 'city': 'tag'}
-        ]))
-
-    def test_DELETE_command(self):
-        " deleted records are not returned at all. "
-        ids = [
-            self.partner.create(self.cr, UID, {'name': 'foo'}),
-            self.partner.create(self.cr, UID, {'name': 'bar'}),
-            self.partner.create(self.cr, UID, {'name': 'baz'})
-        ]
-        commands = [DELETE(ids[0]), DELETE(ids[1]), DELETE(ids[2])]
-
-        results = self.partner.resolve_2many_commands(
-            self.cr, UID, 'child_ids', commands, ['name'])
-
-        self.assertEqual(results, [])
-
-    def test_mixed_commands(self):
-        ids = [
-            self.partner.create(self.cr, UID, {'name': name})
-            for name in ['NObar', 'baz', 'qux', 'NOquux', 'NOcorge', 'garply']
-        ]
-
-        results = self.partner.resolve_2many_commands(
-            self.cr, UID, 'child_ids', [
-                CREATE({'name': 'foo'}),
-                UPDATE(ids[0], {'name': 'bar'}),
-                LINK_TO(ids[1]),
-                DELETE(ids[2]),
-                UPDATE(ids[3], {'name': 'quux',}),
-                UPDATE(ids[4], {'name': 'corge'}),
-                CREATE({'name': 'grault'}),
-                LINK_TO(ids[5])
-            ], ['name'])
-
-        self.assertEqual(sorted_by_id(results), sorted_by_id([
-            {'name': 'foo'},
-            {'id': ids[0], 'name': 'bar'},
-            {'id': ids[1], 'name': 'baz'},
-            {'id': ids[3], 'name': 'quux'},
-            {'id': ids[4], 'name': 'corge'},
-            {'name': 'grault'},
-            {'id': ids[5], 'name': 'garply'}
-        ]))
-
-    def test_LINK_TO_pairs(self):
-        "LINK_TO commands can be written as pairs, instead of triplets"
-        ids = [
-            self.partner.create(self.cr, UID, {'name': 'foo'}),
-            self.partner.create(self.cr, UID, {'name': 'bar'}),
-            self.partner.create(self.cr, UID, {'name': 'baz'})
-        ]
-        commands = map(lambda id: (4, id), ids)
-
-        results = self.partner.resolve_2many_commands(
-            self.cr, UID, 'child_ids', commands, ['name'])
-
-        self.assertEqual(sorted_by_id(results), sorted_by_id([
-            {'id': ids[0], 'name': 'foo'},
-            {'id': ids[1], 'name': 'bar'},
-            {'id': ids[2], 'name': 'baz'}
-        ]))
-
-    def test_singleton_commands(self):
-        "DELETE_ALL can appear as a singleton"
-        results = self.partner.resolve_2many_commands(
-            self.cr, UID, 'child_ids', [DELETE_ALL()], ['name'])
-
-        self.assertEqual(results, [])
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/test_osv.py b/openerp/tests/test_osv.py
deleted file mode 100644 (file)
index e36495f..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-#    OpenERP, Open Source Management Solution
-#    Copyright (C) 2010 OpenERP S.A. http://www.openerp.com
-#
-#    This program is free software: you can redistribute it and/or modify
-#    it under the terms of the GNU Affero General Public License as
-#    published by the Free Software Foundation, either version 3 of the
-#    License, or (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU Affero General Public License for more details.
-#
-#    You should have received a copy of the GNU Affero General Public License
-#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-
-import unittest
-from openerp.osv.query import Query
-
-
-class QueryTestCase(unittest.TestCase):
-
-    def test_basic_query(self):
-        query = Query()
-        query.tables.extend(['"product_product"', '"product_template"'])
-        query.where_clause.append("product_product.template_id = product_template.id")
-        query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False)  # add normal join
-        query.add_join(("product_product", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True)  # outer join
-        self.assertEquals(query.get_sql()[0].strip(),
-            """"product_product" LEFT JOIN "res_user" as "product_product__user_id" ON ("product_product"."user_id" = "product_product__user_id"."id"),"product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") """.strip())
-        self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip())
-
-    def test_query_chained_explicit_joins(self):
-        query = Query()
-        query.tables.extend(['"product_product"', '"product_template"'])
-        query.where_clause.append("product_product.template_id = product_template.id")
-        query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False)  # add normal join
-        query.add_join(("product_template__categ_id", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True)  # CHAINED outer join
-        self.assertEquals(query.get_sql()[0].strip(),
-            """"product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id")""".strip())
-        self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip())
-
-    def test_mixed_query_chained_explicit_implicit_joins(self):
-        query = Query()
-        query.tables.extend(['"product_product"', '"product_template"'])
-        query.where_clause.append("product_product.template_id = product_template.id")
-        query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False)  # add normal join
-        query.add_join(("product_template__categ_id", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True)  # CHAINED outer join
-        query.tables.append('"account.account"')
-        query.where_clause.append("product_category.expense_account_id = account_account.id")  # additional implicit join
-        self.assertEquals(query.get_sql()[0].strip(),
-            """"product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id"),"account.account" """.strip())
-        self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id AND product_category.expense_account_id = account_account.id""".strip())
-
-    def test_raise_missing_lhs(self):
-        query = Query()
-        query.tables.append('"product_product"')
-        self.assertRaises(AssertionError, query.add_join, ("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False)
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/test_qweb.py b/openerp/tests/test_qweb.py
deleted file mode 100644 (file)
index 48e8dc3..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-# -*- coding: utf-8 -*-
-import cgi
-from xml.dom import minidom as dom
-
-import common
-from openerp.addons.base.ir import ir_qweb
-
-impl = dom.getDOMImplementation()
-document = impl.createDocument(None, None, None)
-
-class TestQWebTField(common.TransactionCase):
-    def setUp(self):
-        super(TestQWebTField, self).setUp()
-        self.engine = self.registry('ir.qweb')
-
-    def context(self, values):
-        return ir_qweb.QWebContext(self.cr, self.uid, values)
-
-    def test_trivial(self):
-        field = document.createElement('span')
-        field.setAttribute('t-field', u'company.name')
-
-        Companies = self.registry('res.company')
-        company_id = Companies.create(self.cr, self.uid, {
-            'name': "My Test Company"
-        })
-        result = self.engine.render_node(field, self.context({
-            'company': Companies.browse(self.cr, self.uid, company_id),
-        }))
-
-        self.assertEqual(
-            result,
-            '<span data-oe-model="res.company" data-oe-id="%d" '
-                  'data-oe-field="name" data-oe-type="char" '
-                  'data-oe-expression="company.name">%s</span>' % (
-                company_id,
-                "My Test Company",))
-
-    def test_i18n(self):
-        field = document.createElement('span')
-        field.setAttribute('t-field', u'company.name')
-
-        Companies = self.registry('res.company')
-        s = u"Testing «ταБЬℓσ»: 1<2 & 4+1>3, now 20% off!"
-        company_id = Companies.create(self.cr, self.uid, {
-            'name': s,
-        })
-        result = self.engine.render_node(field, self.context({
-            'company': Companies.browse(self.cr, self.uid, company_id),
-        }))
-
-        self.assertEqual(
-            result,
-            '<span data-oe-model="res.company" data-oe-id="%d" '
-                  'data-oe-field="name" data-oe-type="char" '
-                  'data-oe-expression="company.name">%s</span>' % (
-                company_id,
-                cgi.escape(s.encode('utf-8')),))
-
-    def test_reject_crummy_tags(self):
-        field = document.createElement('td')
-        field.setAttribute('t-field', u'company.name')
-
-        with self.assertRaisesRegexp(
-                AssertionError,
-                r'^RTE widgets do not work correctly'):
-            self.engine.render_node(field, self.context({
-                'company': None
-            }))
-
-    def test_reject_t_tag(self):
-        field = document.createElement('t')
-        field.setAttribute('t-field', u'company.name')
-
-        with self.assertRaisesRegexp(
-                AssertionError,
-                r'^t-field can not be used on a t element'):
-            self.engine.render_node(field, self.context({
-                'company': None
-            }))
diff --git a/openerp/tests/test_translate.py b/openerp/tests/test_translate.py
deleted file mode 100644 (file)
index d9f771b..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-#    OpenERP, Open Source Management Solution
-#    Copyright (C) 2010 OpenERP S.A. http://www.openerp.com
-#
-#    This program is free software: you can redistribute it and/or modify
-#    it under the terms of the GNU Affero General Public License as
-#    published by the Free Software Foundation, either version 3 of the
-#    License, or (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU Affero General Public License for more details.
-#
-#    You should have received a copy of the GNU Affero General Public License
-#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-
-import unittest
-from openerp.tools.translate import quote, unquote
-
-class TranslationToolsTestCase(unittest.TestCase):
-
-    def test_quote_unquote(self):
-
-        def test_string(str):
-            quoted = quote(str)
-            #print "\n1:", repr(str)
-            #print "2:", repr(quoted)
-            unquoted = unquote("".join(quoted.split('"\n"')))
-            #print "3:", repr(unquoted)
-            self.assertEquals(str, unquoted)
-
-        test_string("""test \nall kinds\n \n o\r
-         \\\\ nope\n\n"
-         """)
-
-        # The ones with 1+ backslashes directly followed by
-        # a newline or literal N can fail... we would need a
-        # state-machine parser to handle these, but this would
-        # be much slower so it's better to avoid them at the moment
-        self.assertRaises(AssertionError, quote, """test \nall kinds\n\no\r
-         \\\\nope\n\n"
-         """)
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/test_view_validation.py b/openerp/tests/test_view_validation.py
deleted file mode 100644 (file)
index 4cd8b95..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-# This test can be run stand-alone with something like:
-# > PYTHONPATH=. python2 openerp/tests/test_view_validation.py
-from lxml import etree
-from StringIO import StringIO
-import unittest2
-
-from openerp.tools.view_validation import (valid_page_in_book, valid_att_in_form, valid_type_in_colspan,
-                                           valid_type_in_col, valid_att_in_field, valid_att_in_label,
-                                           valid_field_in_graph, valid_field_in_tree
-                                           )
-
-invalid_form = etree.parse(StringIO('''\
-<form>
-    <label></label>
-    <group>
-        <div>
-            <page></page>
-            <label colspan="True"></label>
-            <field></field>
-        </div>
-    </group>
-    <notebook>
-        <page>
-            <group col="Two">
-            <div>
-                <label></label>
-                <field colspan="Five"> </field>
-                </div>
-            </group>
-        </page>
-    </notebook>
-</form>
-''')).getroot()
-
-valid_form = etree.parse(StringIO('''\
-<form string="">
-    <field name=""></field>
-    <field name=""></field>
-    <notebook>
-        <page>
-            <field name=""></field>
-            <label string=""></label>
-            <field name=""></field>
-        </page>
-        <page>
-            <group colspan="5" col="2">
-                <label for=""></label>
-                <label string="" colspan="5"></label>
-            </group>
-        </page>
-    </notebook>
-</form>
-''')).getroot()
-
-invalid_graph = etree.parse(StringIO('''\
-<graph>
-    <label/>
-    <group>
-        <div>
-            <field></field>
-            <field></field>
-        </div>
-    </group>
-</graph>
-''')).getroot()
-
-valid_graph = etree.parse(StringIO('''\
-<graph string="">
-    <field name=""></field>
-    <field name=""></field>
-</graph>
-''')).getroot()
-
-invalid_tree = etree.parse(StringIO('''\
-<tree>
-  <group>
-    <div>
-      <field></field>
-      <field></field>
-    </div>
-  </group>
-</tree>
-''')).getroot()
-
-valid_tree = etree.parse(StringIO('''\
-<tree string="">
-    <field name=""></field>
-    <field name=""></field>
-    <button/>
-    <field name=""></field>
-</tree>
-''')).getroot()
-
-
-class test_view_validation(unittest2.TestCase):
-    """ Test the view validation code (but not the views themselves). """
-
-    def test_page_validation(self):
-        assert not valid_page_in_book(invalid_form)
-        assert valid_page_in_book(valid_form)
-
-    def test_all_field_validation(self):
-        assert not valid_att_in_field(invalid_form)
-        assert valid_att_in_field(valid_form)
-
-    def test_all_label_validation(self):
-        assert not valid_att_in_label(invalid_form)
-        assert valid_att_in_label(valid_form)
-
-    def test_form_string_validation(self):
-        assert valid_att_in_form(valid_form)
-
-    def test_graph_validation(self):
-        assert not valid_field_in_graph(invalid_graph)
-        assert valid_field_in_graph(valid_graph)
-
-    def test_tree_validation(self):
-        assert not valid_field_in_tree(invalid_tree)
-        assert valid_field_in_tree(valid_tree)
-
-    def test_colspan_datatype_validation(self):
-        assert not valid_type_in_colspan(invalid_form)
-        assert valid_type_in_colspan(valid_form)
-
-    def test_col_datatype_validation(self):
-        assert not valid_type_in_col(invalid_form)
-        assert valid_type_in_col(valid_form)
-
-
-if __name__ == '__main__':
-    unittest2.main()
diff --git a/openerp/tests/test_xmlrpc.py b/openerp/tests/test_xmlrpc.py
deleted file mode 100644 (file)
index 12bc828..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-# -*- coding: utf-8 -*-
-# Run with one of these commands:
-#    > OPENERP_ADDONS_PATH='../../addons/trunk' OPENERP_PORT=8069 \
-#      OPENERP_DATABASE=yy PYTHONPATH=. python tests/test_xmlrpc.py
-#    > OPENERP_ADDONS_PATH='../../addons/trunk' OPENERP_PORT=8069 \
-#      OPENERP_DATABASE=yy nosetests tests/test_xmlrpc.py
-#    > OPENERP_ADDONS_PATH='../../../addons/trunk' OPENERP_PORT=8069 \
-#      OPENERP_DATABASE=yy PYTHONPATH=../:. unit2 test_xmlrpc
-import time
-import unittest2
-import xmlrpclib
-
-import common
-
-DB = None
-ADMIN_USER = common.ADMIN_USER
-ADMIN_USER_ID = common.ADMIN_USER_ID
-ADMIN_PASSWORD = common.ADMIN_PASSWORD
-
-def setUpModule():
-    common.start_openerp()
-    global DB
-    DB = common.RpcCase.generate_database_name()
-
-tearDownModule = common.stop_openerp
-
-class test_xmlrpc(common.RpcCase):
-
-    def test_00_xmlrpc_create_database_polling(self):
-        """
-        Simulate a OpenERP client requesting the creation of a database and
-        polling the server until the creation is complete.
-        """
-        progress_id = self.proxy.db_60.create(ADMIN_PASSWORD,DB, True, False,
-            ADMIN_PASSWORD)
-        while True:
-            time.sleep(1)
-            progress, users = self.proxy.db_60.get_progress(ADMIN_PASSWORD,
-                progress_id)
-            if progress == 1.0:
-                break
-
-    def test_xmlrpc_login(self):
-        """ Try to login on the common service. """
-        uid = self.proxy.common_60.login(DB, ADMIN_USER, ADMIN_PASSWORD)
-        assert uid == ADMIN_USER_ID
-
-    def test_xmlrpc_ir_model_search(self):
-        """ Try a search on the object service. """
-        o = self.proxy.object_60
-        ids = o.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, 'ir.model', 'search', [])
-        assert ids
-        ids = o.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, 'ir.model', 'search', [], {})
-        assert ids
-
-    def test_xmlrpc_8_ir_model_search(self):
-        """ Try a search on the object service. """
-        o = self.proxy.object_8
-        ids = o.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, 'ir.model', 'search', [])
-        assert ids
-        ids = o.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, 'ir.model', 'search', [], {})
-        assert ids
-
-    # This test was written to test the creation of a new RPC endpoint, not
-    # really for the EDI itself.
-    #def test_xmlrpc_import_edi_document(self):
-    #    """ Try to call an EDI method. """
-    #    msg_re = 'EDI Document is empty!'
-    #    with self.assertRaisesRegexp(Exception, msg_re):
-    #        self.proxy.edi_60.import_edi_document(DB, ADMIN_USER_ID, ADMIN_PASSWORD, {})
-
-    def test_zz_xmlrpc_drop_database(self):
-        """
-        Simulate a OpenERP client requesting the deletion of a database.
-        """
-        assert self.proxy.db_60.drop(ADMIN_PASSWORD, DB) is True
-
-if __name__ == '__main__':
-    unittest2.main()
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 10c3663..060d261 100644 (file)
@@ -321,7 +321,6 @@ class YamlInterpreter(object):
                 view_info = model.fields_view_get(self.cr, SUPERUSER_ID, varg, 'form', context)
 
             record_dict = self._create_record(model, fields, view_info, default=default)
-            _logger.debug("RECORD_DICT %s" % record_dict)
             id = self.pool['ir.model.data']._update(self.cr, SUPERUSER_ID, record.model, \
                     self.module, record_dict, record.id, noupdate=self.isnoupdate(record), mode=self.mode, context=context)
             self.id_map[record.id] = int(id)
@@ -931,7 +930,7 @@ class YamlInterpreter(object):
 def yaml_import(cr, module, yamlfile, kind, idref=None, mode='init', noupdate=False, report=None):
     if idref is None:
         idref = {}
-    loglevel = logging.INFO if kind == 'test' else logging.DEBUG
+    loglevel = logging.DEBUG
     yaml_string = yamlfile.read()
     yaml_interpreter = YamlInterpreter(cr, module, idref, mode, filename=yamlfile.name, report=report, noupdate=noupdate, loglevel=loglevel)
     yaml_interpreter.process(yaml_string)