[IMP] cleanups, move tests, remove deprecated model instantiation
authorAntony Lesuisse <al@openerp.com>
Thu, 1 May 2014 18:42:17 +0000 (20:42 +0200)
committerAntony Lesuisse <al@openerp.com>
Thu, 1 May 2014 18:42:17 +0000 (20:42 +0200)
bzr revid: al@openerp.com-20140501184217-pgi1gzbdpysdybog

15 files changed:
openerp/addons/base/__openerp__.py
openerp/addons/base/ir/ir_default.py
openerp/addons/base/ir/ir_exports.py
openerp/addons/base/ir/ir_filters.py
openerp/addons/base/ir/ir_rule.py
openerp/addons/base/res/ir_property.py
openerp/addons/base/res/res_bank.py
openerp/addons/base/res/res_currency.py
openerp/addons/base/test/base_test.yml [deleted file]
openerp/addons/base/test/test_auth.yml [deleted file]
openerp/addons/base/test/test_ir_rule.yml [deleted file]
openerp/addons/base/test/test_osv_expression.yml [deleted file]
openerp/addons/base/tests/base_test.yml [new file with mode: 0644]
openerp/addons/base/tests/test_ir_rule.yml [new file with mode: 0644]
openerp/addons/base/tests/test_osv_expression.yml [new file with mode: 0644]

index d38ac7c..f5a9ab5 100644 (file)
@@ -93,9 +93,9 @@ The kernel of OpenERP, needed for all installation.
         'res/res_partner_image_demo.xml',
     ],
     'test': [
-        'test/base_test.yml',
-        'test/test_osv_expression.yml',
-        'test/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules.
+        'tests/base_test.yml',
+        'tests/test_osv_expression.yml',
+        'tests/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules.
     ],
     'installable': True,
     'auto_install': True,
index 21c66c0..3b82b3a 100644 (file)
@@ -43,7 +43,5 @@ class ir_default(osv.osv):
     _defaults = {
         'company_id': _get_company_id,
     }
-ir_default()
-
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 972a21c..c6d188b 100644 (file)
@@ -31,7 +31,6 @@ class ir_exports(osv.osv):
         'export_fields': fields.one2many('ir.exports.line', 'export_id',
                                          'Export ID'),
     }
-ir_exports()
 
 
 class ir_exports_line(osv.osv):
@@ -41,7 +40,6 @@ class ir_exports_line(osv.osv):
         'name': fields.char('Field Name', size=64),
         'export_id': fields.many2one('ir.exports', 'Export', select=True, ondelete='cascade'),
     }
-ir_exports_line()
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
 
index f302fe5..418c307 100644 (file)
@@ -142,6 +142,4 @@ class ir_filters(osv.osv):
         'is_default': False
     }
 
-ir_filters()
-
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 9926fd6..96b7da4 100644 (file)
@@ -172,7 +172,4 @@ class ir_rule(osv.osv):
         self.clear_cache(cr,uid)
         return res
 
-ir_rule()
-
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
index 2a75136..543349c 100644 (file)
@@ -177,9 +177,4 @@ class ir_property(osv.osv):
                   '|', ('company_id', '=', cid), ('company_id', '=', False)]
         return domain
 
-ir_property()
-
-
-
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
index 00d18d0..cc29c50 100644 (file)
@@ -51,9 +51,6 @@ class Bank(osv.osv):
             result.append((bank.id, (bank.bic and (bank.bic + ' - ') or '') + bank.name))
         return result
 
-Bank()
-
-
 class res_partner_bank_type(osv.osv):
     _description='Bank Account Type'
     _name = 'res.partner.bank.type'
@@ -67,7 +64,6 @@ class res_partner_bank_type(osv.osv):
     _defaults = {
         'format_layout': lambda *args: "%(bank_name)s: %(acc_number)s"
     }
-res_partner_bank_type()
 
 class res_partner_bank_type_fields(osv.osv):
     _description='Bank type fields'
@@ -80,8 +76,6 @@ class res_partner_bank_type_fields(osv.osv):
         'readonly': fields.boolean('Readonly'),
         'size': fields.integer('Max. Size'),
     }
-res_partner_bank_type_fields()
-
 
 class res_partner_bank(osv.osv):
     '''Bank Accounts'''
@@ -234,6 +228,4 @@ class res_partner_bank(osv.osv):
             result['state_id'] = part.state_id.id
         return {'value': result}
 
-res_partner_bank()
-
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 1944478..d92969b 100644 (file)
@@ -235,8 +235,6 @@ class res_currency(osv.osv):
             else:
                 return from_amount * rate
 
-res_currency()
-
 class res_currency_rate_type(osv.osv):
     _name = "res.currency.rate.type"
     _description = "Currency Rate Type"
@@ -244,8 +242,6 @@ class res_currency_rate_type(osv.osv):
         'name': fields.char('Name', size=64, required=True, translate=True),
     }
 
-res_currency_rate_type()
-
 class res_currency_rate(osv.osv):
     _name = "res.currency.rate"
     _description = "Currency Rate"
@@ -261,7 +257,5 @@ class res_currency_rate(osv.osv):
     }
     _order = "name desc"
 
-res_currency_rate()
-
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
 
diff --git a/openerp/addons/base/test/base_test.yml b/openerp/addons/base/test/base_test.yml
deleted file mode 100644 (file)
index e73d817..0000000
+++ /dev/null
@@ -1,318 +0,0 @@
--   |
-    To check that common dangerous operations are not allowed by the safe_eval mechanism, attempt to 
-    evaluate unauthorized expressions, and verify that they trigger an error.
--
-    1. Try a few common expressions to verify they work with safe_eval
--
-    !python {model: ir.model}: |
-        from openerp.tools.safe_eval import safe_eval
-        expected = (1, {"a": 9 * 2}, (True, False, None))
-        actual = safe_eval('(1, {"a": 9 * 2}, (True, False, None))')
-        assert actual == expected, "Simple python expressions are not working with safe_eval"
--
-    2. Try simple literal definition to verify it works with literal_eval
--
-    !python {model: ir.model}: |
-        import ast
-        expected = (1, {"a": 9}, (True, False, None))
-        actual = ast.literal_eval('(1, {"a": 9}, (True, False, None))')
-        assert actual == expected, "Simple python expressions are not working with literal_eval"
--
-    3. Try arithmetic expression in literal_eval to verify it does not work
--
-    !python {model: ir.model}: |
-        import ast
-        try:
-           ast.literal_eval('(1, {"a": 2*9}, (True, False, None))')
-           assert False, "literal_eval should not accept arithmetic expressions"
-        except ValueError:
-           pass
--
-    4. Try forbidden expressions in literal_eval to verify they are not allowed
--
-    !python {model: ir.model}: |
-        import ast
-        try:
-           ast.literal_eval('{"a": True.__class__}')
-           assert False, "literal_eval should accept only literals"
-        except ValueError:
-           pass
--
-    5. Try forbidden expressions in safe_eval to verify they are not allowed (open)
--
-    !python {model: ir.model}: |
-        from openerp.tools.safe_eval import safe_eval
-        from openerp.tools.misc import mute_logger
-        try:
-            with mute_logger('openerp.tools.safe_eval'):
-                safe_eval('open("/etc/passwd","r")')
-            assert False, "safe_eval should not allow calling open() builtin"
-        except ValueError:
-            pass
-
--
-    "ORM test: verify that parent_store computation are going right"
--
-    0. Emulate normal behavior of tree structure storing
--
-    !python {model: res.partner.category}: |
-        # pretend the pool has finished loading to avoid deferring parent_store computation
-        self.pool._init = False
--
-    "1.0 Setup test partner categories: parent root"
--
-    !record {model: res.partner.category, id: test_categ_root}:
-        name: Root category
--
-    "1.1 Setup test partner categories: parent category"
--
-    !record {model: res.partner.category, id: test_categ_0}:
-        name: Parent category
-        parent_id: test_categ_root
--
-    "1.2 Setup test partner categories: child 1"
--
-    !record {model: res.partner.category, id: test_categ_1}:
-        name: Child 1
-        parent_id: test_categ_0
--
-    "1.3 Setup test partner categories: child 2"
--
-    !record {model: res.partner.category, id: test_categ_2}:
-        name: Child 2
-        parent_id: test_categ_0
--
-    "1.4 Setup test partner categories: child 2-1"
--
-    !record {model: res.partner.category, id: test_categ_21}:
-        name: Child 2-1
-        parent_id: test_categ_2
--
-    2. Duplicate the parent category and verify that the children have been duplicated too and are below the new parent
--
-    !python {model: res.partner.category}: |
-        new_id = self.copy(cr, uid, ref('test_categ_0'))
-        new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
-        assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
-        old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
-        assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
-        assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
--
-    3. Duplicate the children then reassign them to the new parent (1st method) and check the parent_store structure.
--
-    !python {model: res.partner.category}: |
-        new_child1_id = self.copy(cr, uid, ref('test_categ_1'))
-        new_child2_id = self.copy(cr, uid, ref('test_categ_2'))
-        new_id = self.copy(cr, uid, ref('test_categ_0'), {'child_ids': []})
-        self.write(cr, uid, [new_child1_id, new_child2_id], {'parent_id': new_id})
-        new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
-        assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
-        old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
-        assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
-        assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
--
-    4. Duplicate the children then reassign them to the new parent (2nd method) and check the parent_store structure.
--
-    !python {model: res.partner.category}: |
-        new_child1_id = self.copy(cr, uid, ref('test_categ_1'))
-        new_child2_id = self.copy(cr, uid, ref('test_categ_2'))
-        old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
-        new_id = self.copy(cr, uid, ref('test_categ_0'), {'child_ids': [(6,0,[new_child1_id, new_child2_id])]})
-        new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
-        assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
-        old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
-        assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
-        assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
--
-    5. Duplicate the children then reassign them to the new parent (3rd method) and make sure the parent_store structure is still right.
--
-    !python {model: res.partner.category}: |
-        new_child1_id = self.copy(cr, uid, ref('test_categ_1'))
-        new_child2_id = self.copy(cr, uid, ref('test_categ_2'))
-        new_id = self.copy(cr, uid, ref('test_categ_0'), {'child_ids': []})
-        self.write(cr, uid, [new_id], {'child_ids': [(4,new_child1_id), (4,new_child2_id)]})
-        new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
-        assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
-        old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
-        assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
-        assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
--
-    6. Restore pool state after the test
--
-    !python {model: res.partner.category}: |
-        self.pool._init = True
-
--
-    "Float precision tests: verify that float rounding methods are working correctly via res.currency"
--
-    !python {model: res.currency}: |
-        from openerp.tools import float_repr
-        from math import log10
-        currency = self.browse(cr, uid, ref('base.EUR'))
-        def try_round(amount, expected, self=self, cr=cr, currency=currency, float_repr=float_repr,
-                      log10=log10):
-            digits = max(0,-int(log10(currency.rounding)))
-            result = float_repr(self.round(cr, 1, currency, amount), precision_digits=digits)
-            assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
-        try_round(2.674,'2.67')
-        try_round(2.675,'2.68')   # in Python 2.7.2, round(2.675,2) gives 2.67
-        try_round(-2.675,'-2.68') # in Python 2.7.2, round(2.675,2) gives 2.67
-        try_round(0.001,'0.00')
-        try_round(-0.001,'-0.00')
-        try_round(0.0049,'0.00')   # 0.0049 is closer to 0 than to 0.01, so should round down
-        try_round(0.005,'0.01')   # the rule is to round half away from zero
-        try_round(-0.005,'-0.01') # the rule is to round half away from zero
-
-        def try_zero(amount, expected, self=self, cr=cr, currency=currency):
-            assert self.is_zero(cr, 1, currency, amount) == expected, "Rounding error: %s should be zero!" % amount
-        try_zero(0.01, False)
-        try_zero(-0.01, False)
-        try_zero(0.001, True)
-        try_zero(-0.001, True)
-        try_zero(0.0046, True)
-        try_zero(-0.0046, True)
-        try_zero(2.68-2.675, False) # 2.68 - 2.675 = 0.005 -> rounds to 0.01
-        try_zero(2.68-2.676, True)  # 2.68 - 2.675 = 0.004 -> rounds to 0.0
-        try_zero(2.676-2.68, True)  # 2.675 - 2.68 = -0.004 -> rounds to -0.0
-        try_zero(2.675-2.68, False) # 2.675 - 2.68 = -0.005 -> rounds to -0.01
-
-        def try_compare(amount1, amount2, expected, self=self, cr=cr, currency=currency):
-            assert self.compare_amounts(cr, 1, currency, amount1, amount2) == expected, \
-                "Rounding error, compare_amounts(%s,%s) should be %s" % (amount1, amount2, expected)
-        try_compare(0.001, 0.001, 0)
-        try_compare(-0.001, -0.001, 0)
-        try_compare(0.001, 0.002, 0)
-        try_compare(-0.001, -0.002, 0)
-        try_compare(2.675, 2.68, 0)
-        try_compare(2.676, 2.68, 0)
-        try_compare(-2.676, -2.68, 0)
-        try_compare(2.674, 2.68, -1)
-        try_compare(-2.674, -2.68, 1)
-        try_compare(3, 2.68, 1)
-        try_compare(-3, -2.68, -1)
-        try_compare(0.01, 0, 1)
-        try_compare(-0.01, 0, -1)
-
--
-    "Float precision tests: verify that float rounding methods are working correctly via tools"
--
-    !python {model: res.currency}: |
-        from openerp.tools import float_compare, float_is_zero, float_round, float_repr
-        def try_round(amount, expected, precision_digits=3, float_round=float_round, float_repr=float_repr):
-            result = float_repr(float_round(amount, precision_digits=precision_digits),
-                                precision_digits=precision_digits)
-            assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
-        try_round(2.6745, '2.675')
-        try_round(-2.6745, '-2.675')
-        try_round(2.6744, '2.674')
-        try_round(-2.6744, '-2.674')
-        try_round(0.0004, '0.000')
-        try_round(-0.0004, '-0.000')
-        try_round(357.4555, '357.456')
-        try_round(-357.4555, '-357.456')
-        try_round(457.4554, '457.455')
-        try_round(-457.4554, '-457.455')
-
-        # Extended float range test, inspired by Cloves Almeida's test on bug #882036.
-        fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555]
-        expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556']
-        precisions = [2, 2, 2, 2, 2, 2, 3, 4]
-        # Note: max precision for double floats is 53 bits of precision or
-        # 17 significant decimal digits
-        for magnitude in range(7):
-            for i in xrange(len(fractions)):
-                frac, exp, prec = fractions[i], expecteds[i], precisions[i]
-                for sign in [-1,1]:
-                    for x in xrange(0,10000,97):
-                        n = x * 10**magnitude
-                        f = sign * (n + frac)
-                        f_exp = ('-' if f != 0 and sign == -1 else '') + str(n) + exp 
-                        try_round(f, f_exp, precision_digits=prec)
-
-
-        def try_zero(amount, expected, float_is_zero=float_is_zero):
-            assert float_is_zero(amount, precision_digits=3) == expected, "Rounding error: %s should be zero!" % amount
-        try_zero(0.0002, True)
-        try_zero(-0.0002, True)
-        try_zero(0.00034, True)
-        try_zero(0.0005, False)
-        try_zero(-0.0005, False)
-        try_zero(0.0008, False)
-        try_zero(-0.0008, False)
-
-        def try_compare(amount1, amount2, expected, float_compare=float_compare):
-            assert float_compare(amount1, amount2, precision_digits=3) == expected, \
-                "Rounding error, compare_amounts(%s,%s) should be %s" % (amount1, amount2, expected)
-        try_compare(0.0003, 0.0004, 0)
-        try_compare(-0.0003, -0.0004, 0)
-        try_compare(0.0002, 0.0005, -1)
-        try_compare(-0.0002, -0.0005, 1)
-        try_compare(0.0009, 0.0004, 1)
-        try_compare(-0.0009, -0.0004, -1)
-        try_compare(557.4555, 557.4556, 0)
-        try_compare(-557.4555, -557.4556, 0)
-        try_compare(657.4444, 657.445, -1)
-        try_compare(-657.4444, -657.445, 1)
-
-        # Rounding to unusual rounding units (e.g. coin values)
-        def try_round(amount, expected, precision_rounding=None, float_round=float_round, float_repr=float_repr):
-            result = float_repr(float_round(amount, precision_rounding=precision_rounding),
-                                precision_digits=2)
-            assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
-        try_round(-457.4554, '-457.45', precision_rounding=0.05)
-        try_round(457.444, '457.50', precision_rounding=0.5)
-        try_round(457.3, '455.00', precision_rounding=5)
-        try_round(457.5, '460.00', precision_rounding=5)
-        try_round(457.1, '456.00', precision_rounding=3)
-
--
-    "Float precision tests: check that proper rounding is performed for float persistence"
--
-    !python {model: res.currency}: |
-        currency = self.browse(cr, uid, ref('base.EUR'))
-        res_currency_rate = self.pool.get('res.currency.rate')
-        from openerp.tools import float_compare, float_is_zero, float_round, float_repr
-        def try_roundtrip(value, expected, self=self, cr=cr, currency=currency,
-                          res_currency_rate=res_currency_rate):
-            rate_id = res_currency_rate.create(cr, 1, {'name':'2000-01-01',
-                                                       'rate': value,
-                                                       'currency_id': currency.id})
-            rate = res_currency_rate.read(cr, 1, rate_id, ['rate'])['rate']
-            assert rate == expected, 'Roundtrip error: got %s back from db, expected %s' % (rate, expected)
-        # res.currency.rate uses 6 digits of precision by default
-        try_roundtrip(2.6748955, 2.674896)
-        try_roundtrip(-2.6748955, -2.674896)
-        try_roundtrip(10000.999999, 10000.999999)
-        try_roundtrip(-10000.999999, -10000.999999)
-
--
-    "Float precision tests: verify that invalid parameters are forbidden"
--
-    !python {model: res.currency}: |
-        from openerp.tools import float_compare, float_is_zero, float_round
-        try:
-            float_is_zero(0.01, precision_digits=3, precision_rounding=0.01)
-        except AssertionError:
-            pass
-        try:
-            float_compare(0.01, 0.02, precision_digits=3, precision_rounding=0.01)
-        except AssertionError:
-            pass
-        try:
-            float_round(0.01, precision_digits=3, precision_rounding=0.01)
-        except AssertionError:
-            pass
--
-   Test res.groups name search
--
-     !python {model: res.groups}: |
-         all_groups = self.search(cr, uid, [])
-         full_names = [(group.id, group.full_name) for group in self.browse(cr, uid, all_groups)]
-         group_ids = self.search(cr, uid, [('full_name', 'like', '%Sale%')])
-         assert set(group_ids) == set([id for (id, full_name) in full_names if 'Sale' in full_name]), "did not match search for 'Sale'"
-         group_ids = self.search(cr, uid, [('full_name', 'like', '%Technical%')])
-         assert set(group_ids) == set([id for (id, full_name) in full_names if 'Technical' in full_name]), "did not match search for 'Technical'"
-         group_ids = self.search(cr, uid, [('full_name', 'like', '%Sales /%')])
-         assert set(group_ids) == set([id for (id, full_name) in full_names if 'Sales /' in full_name]), "did not match search for 'Sales /'"
-         group_ids = self.search(cr, uid, [('full_name', 'in', ['Administration / Access Rights','Contact Creation'])])
-         assert group_ids, "did not match search for 'Administration / Access Rights' and 'Contact Creation'"
diff --git a/openerp/addons/base/test/test_auth.yml b/openerp/addons/base/test/test_auth.yml
deleted file mode 100644 (file)
index 62132f0..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
--
-  I will now stress the authentication layer of the ORM
--
-  I create a test user.
--
-  !record {model: res.users, id: res_user_test_a1}:
-      name: Test Auth User 1
-      login: test_base_a1
-      password: 'base-test-passwd'
-      active: True
--
-  I will prepare the context
--
-  !python {model: res.users }: |
-    from openerp.tools import config
-    host = config.get_misc('httpd', 'interface')
-    port = config.get_misc('httpd', 'port', 8069)
-    if not host:
-        host = config.get('xmlrpc_interface')
-        port = config.get('xmlrpc_port') or self.port
-    if host == '0.0.0.0' or not host:
-        host = '127.0.0.1'
-    port = int(port)
-    context['test_xmlrpc_url'] = 'http://%s:%d/xmlrpc/' % (host, port)
--
-  I will commit the cursor and try to login.
--
-  !python {model: res.users }: |
-    from xmlrpclib import ServerProxy
-    cr.commit()
-    try:
-        logsock = ServerProxy(context['test_xmlrpc_url']+'common')
-        luid = logsock.login(cr.dbname, 'test_base_a1', 'base-test-passwd')
-        assert luid, "User is not activated after res.users commit!"
-    except Exception:
-        raise
--
-  I will just try to read something as that user
--
-  !python {model: res.users }: |
-    from xmlrpclib import ServerProxy
-    cr.commit()
-    try:
-        logsock = ServerProxy(context['test_xmlrpc_url']+'object')
-        luid = ref('res_user_test_a1')
-        res = logsock.execute(cr.dbname, luid, 'base-test-passwd', 'res.users', 'read', luid, ['name',])
-        assert res and res['name'], "User cannot read its name!"
-    except Exception:
-        raise
--
-  I will now disable the user.
--
-  !record {model: res.users, id: res_user_test_a1}:
-      active: False
--
-  I will commit the cursor.
--
-  !python {model: res.users }: |
-    cr.commit()
--
-  I will try to read again, connecting as the disabled user.
--
-  !python {model: res.users }: |
-    from xmlrpclib import ServerProxy
-    cr.commit()
-    try:
-        logsock = ServerProxy(context['test_xmlrpc_url']+'object')
-        luid = ref('res_user_test_a1')
-        res = logsock.execute(cr.dbname, luid, 'base-test-passwd', 'res.users', 'read', luid, ['name',])
-        raise AssertionError("User should not be enabled!")
-    except Fault, e:
-        if e.faultCode != 'AccessDenied':
-            raise
diff --git a/openerp/addons/base/test/test_ir_rule.yml b/openerp/addons/base/test/test_ir_rule.yml
deleted file mode 100644 (file)
index 49b368a..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
--
-    Exercise ir_rule.py code (and indirectly expression.py).
--
-    Create an ir_rule for the Employee group (called base.group_user)
-    with an blank domain.
--
-    !record {model: ir.rule, id: test_rule}:
-        model_id: base.model_res_partner
-        domain_force: False
-        name: test_rule
-        groups:
-            - base.group_user
-        perm_unlink: 1
-        perm_write: 1
-        perm_read: 1
-        perm_create: 1
--
-    Read as demo user the partners (one blank domain).
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see some partner."
--
-    Domain is not empty.
--
-    !record {model: ir.rule, id: test_rule}:
-        model_id: base.model_res_partner
-        domain_force: "[(1,'=',1)]"
--
-    Read as demo user the partners (one 1=1 domain).
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see some partner."
--
-    Domain is an empty list.
--
-    !record {model: ir.rule, id: test_rule}:
-        model_id: base.model_res_partner
-        domain_force: "[]"
--
-    Read as demo user the partners (one [] domain).
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see some partner."
--
-    Create another ir_rule for the Employee group (to test rules from
-    multiple groups).
--
-    !record {model: ir.rule, id: test_rule2}:
-        model_id: base.model_res_partner
-        domain_force: False
-        name: test_rule2
-        groups:
-            - base.group_user
-        perm_unlink: 1
-        perm_write: 1
-        perm_read: 1
-        perm_create: 1
--
-    Read as demo user the partners (blank and [] domains).
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see some partner."
--
-    Domain is not empty.
--
-    !record {model: ir.rule, id: test_rule}:
-        model_id: base.model_res_partner
-        domain_force: "[(1,'=',1)]"
--
-    Read as demo user the partners (1=1 and blank domain).
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see some partner."
--
-    Domain is not empty.
--
-    !record {model: ir.rule, id: test_rule2}:
-        model_id: base.model_res_partner
-        domain_force: "[(1,'=',1)]"
--
-    Read as demo user the partners (two 1=1 domains).
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see some partner."
--
-    Create another ir_rule for the Employee group (to test rules from
-    multiple groups).
--
-    !record {model: ir.rule, id: test_rule3}:
-        model_id: base.model_res_partner
-        domain_force: False
-        name: test_rule3
-        groups:
-            - base.group_user
-        perm_unlink: 1
-        perm_write: 1
-        perm_read: 1
-        perm_create: 1
--
-    Read as demo user the partners.
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see some partner."
--
-    Domain is not empty.
--
-    !record {model: ir.rule, id: test_rule3}:
-        model_id: base.model_res_partner
-        domain_force: "[(1,'=',1)]"
--
-    Read as demo user the partners (three 1=1 domains).
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see some partner."
--
-    Modify the global rule on res_company which triggers a recursive check
-    of the rules on company.
--
-    !record {model: ir.rule, id: base.res_company_rule}:
-        domain_force: "[('id','child_of',[user.company_id.id])]"
--
-    Read as demo user the partners (exercising the global company rule).
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see some partner."
--
-    Modify the ir_rule for employee to have a rule that fordids
-    seeing any record.
-    We use a domain with implicit AND operator for later tests
-    on normalization.
--
-    !record {model: ir.rule, id: test_rule2}:
-        domain_force: "[('id','=',False),('name','=',False)]"
--
-    Check that demo user still sees partners, because group-rules are OR'ed.
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see some partner."
--
-    Create a new group with demo user in it, and a complex rule.
-    Demo should still see partners.
--
-    !record {model: res.groups, id: test_group}:
-        name: Test Group
-        users:
-            - base.user_demo
--
-    Add the rule to the new group, with a domain containing an implicit AND operator,
-    which is more tricky because it will have to be normalized before combining it.
--
-    !record {model: ir.rule, id: test_rule3}:
-        model_id: base.model_res_partner
-        domain_force: "[('name', '!=', False),('id', '!=', False)]"
-        name: test_rule4
-        groups:
-            - test_group
-        perm_unlink: 1
-        perm_write: 1
-        perm_read: 1
-        perm_create: 1
--
-    Read the partners again as demo user, which should give results.
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see partners even with the combined rules."
--
-    Delete global domains (to combine only group domains).
--
-    !python {model: ir.rule}: |
-        ids = self.search(cr, uid, [('groups','=',False)])
-        assert ids, "Demo user should see some partner."
-        self.unlink(cr, uid, ids)
--
-    Read as demo user the partners (several group domains, no global domain).
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, ref('base.user_demo'), [])
-        assert ids, "Demo user should see some partner."
diff --git a/openerp/addons/base/test/test_osv_expression.yml b/openerp/addons/base/test/test_osv_expression.yml
deleted file mode 100644 (file)
index e006113..0000000
+++ /dev/null
@@ -1,563 +0,0 @@
--
-    Testing for hierarchical search in M2M
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, uid, [('category_id', 'child_of',ref('res_partner_category_0'))])
-        assert len(ids) >= 1, ids
--
-    "1.0 Setup test partner categories: parent root"
--
-    !record {model: res.partner.category, id: categ_root}:
-        name: Root category
--
-    "1.1 Setup test partner categories: parent category"
--
-    !record {model: res.partner.category, id: categ_0}:
-        name: Parent category
-        parent_id: categ_root
--
-    "1.2 Setup test partner categories: child 1"
--
-    !record {model: res.partner.category, id: categ_1}:
-        name: Child 1
-        parent_id: categ_0
--
-    Test hierarchical search in M2M with child ID (list of ids)
--
-    !python {model: res.partner.category }: |
-        ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_root')])])
-        assert len(ids) == 3, ids
--
-    Test hierarchical search in M2M with child ID (single id)
--
-    !python {model: res.partner.category }: |
-        ids = self.search(cr, uid, [('id', 'child_of',ref('categ_root'))])
-        assert len(ids) == 3, ids
--
-    Test hierarchical search in M2M with child IDs
--
-    !python {model: res.partner.category }: |
-        ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_1'), ref('categ_0')])])
-        assert len(ids) == 2, ids
--
-    Test hierarchical search in M2M with child IDs
--
-    !python {model: res.partner.category }: |
-        ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_0')])])
-        assert len(ids) == 2, ids
--
-    Test hierarchical search in M2M with child IDs
--
-    !python {model: res.partner.category }: |
-        ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_1')])])
-        assert len(ids) == 1, ids
--
-    Testing that some domain expressions work
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, uid, [('parent_id','=','Agrolait')])
-        assert len(ids) >= 1, ids
--
-    Trying the "in" operator, for scalar value
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, uid, [('parent_id','in','Agrolait')])
-        assert len(ids) >= 1, ids
--
-    Trying the "in" operator for list value
--
-    !python {model: res.partner }: |
-        ids = self.search(cr, uid, [('parent_id','in',['Agrolait','ASUStek'])])
-        assert len(ids) >= 1, ids
--
-    Check we can use "in" operator for plain fields.
--
-    !python {model: ir.ui.menu }: |
-        ids = self.search(cr, uid, [('sequence','in',[1, 2, 10, 20])])
-        assert len(ids) >= 1, ids
--
-    Test one2many operator with empty search list
--
-    !assert {model: res.partner, search: "[('child_ids', 'in', [])]", count: 0, string: "Ids should be empty"}
--
-    Test one2many operator with False
--
-    !assert {model: res.partner, search: "[('child_ids', '=', False)]"}:
-        - child_ids in (False, None, [])
--
-    Test many2many operator with empty search list
--
-    !assert {model: res.partner, search: "[('category_id', 'in', [])]", count: 0, string: "Ids should be empty"}
--
-    Test many2many operator with False
--
-    !assert {model: res.partner, search: "[('category_id', '=', False)]"}:
-        - category_id in (False, None, [])
--
-    Filtering on invalid value across x2many relationship should return an empty set
--
-    !assert {model: res.partner, search: "[('child_ids.city','=','foo')]", count: 0, string: "Searching for address.city = foo should give empty results"}
--
-    Check if many2one works with empty search list
--
-    !assert {model: res.partner, search: "[('company_id','in', [])]", count: 0, string: "Searching for company_id in [] should be empty!" }
--
-    For the sake of the following tests, I will create a second company
--
-    !record {model: res.company, id: ymltest_company2}:
-        name: Acme 2
--
-    And create a few partners with that company or no company
--
-    !python {model: res.partner }: |
-        for r in range(4):
-            self.create(cr, uid, { 'name': 'P of Acme %d' % r,
-                    'company_id': ref('ymltest_company2') })
-        for r in range(4):
-            self.create(cr, uid, { 'name': 'P of All %d' % r,
-                    'company_id': False })
--
-    Check if many2one works with negative empty list
--
-    !python {model: res.partner }: |
-        all_ids = self.search(cr, uid, [])
-        all_ids.sort()
-        res_ids = self.search(cr, uid,['|',('company_id','not in', []), ('company_id','=',False)])
-        res_ids.sort()
-        assert all_ids == res_ids, "not in [] fails"
--
-    Check that many2one will pick the correct records with a list
--
-    !python {model: res.partner }: |
-        res_ids = self.search(cr, uid, [('company_id', 'in', [False,])])
-        assert len(res_ids) >= 4, "We created 4 partners w/company, why find %d? %r" % \
-                    (len(res_ids), res_ids)
--
-    Check that many2one will exclude the correct records with a list
--
-    !python {model: res.partner }: |
-        # assuming that the default company is #1
-        res_ids = self.search(cr, uid, [('company_id', 'not in', [1])])
-        assert len(res_ids) >= 4, "We should have found 4 records at least, only have %d! %r" % \
-            (len(res_ids), res_ids)
--
-    Check that we exclude the correct records, + False
--
-    !python {model: res.partner }: |
-        # assuming that the default company is #1
-        res_ids = self.search(cr, uid, ['|', ('company_id', 'not in', [1]), ('company_id', '=', False)])
-        assert len(res_ids) >= 8, "We should have found 8 records at least, only have %d! %r" % \
-            (len(res_ids), res_ids)
--
-    Check that multi-level expressions also work
--
-    !python {model: res.partner }: |
-        res_ids = self.search(cr, uid, [('company_id.partner_id', 'in', [])])
-        assert res_ids == [], "Searching an empty set should return empty result, not %r" % res_ids
--
-    Check that multi-level expressions with negative op work
--
-    !python {model: res.partner }: |
-        all_ids = self.search(cr, uid, [('company_id', '!=', False)])
-        all_ids.sort()
-        res_ids = self.search(cr, uid, [('company_id.partner_id', 'not in', [])])
-        res_ids.sort()
-        assert res_ids == all_ids, "Searching against empty set failed, returns %r" % res_ids
--
-    Test the '(not) like/in' behavior. res.partner and its parent_id column are used because
-    parent_id is a many2one, allowing to test the Null value, and there are actually some
-    null and non-null values in the demo data.
--
-    !python {model: res.partner }: |
-        partner_ids = self.search(cr, uid, [])
-        partner_ids.sort()
-        max_partner_id = max(partner_ids)
-
-        # Grab test sample data without using a normal
-        # search domain, because we want to test these later,
-        # so we can't rely on them!
-        partners = self.browse(cr, uid, partner_ids)
-        with_parent = []
-        without_parent = []
-        with_website = []
-        for x in partners:
-            if x.parent_id:
-                with_parent.append(x.id)
-            else:
-                without_parent.append(x.id)
-            if x.website:
-                with_website.append(x.id)
-        with_parent.sort()
-        without_parent.sort()
-        with_website.sort()
-
-        # We treat null values differently than in SQL. For instance in SQL:
-        #   SELECT id FROM res_partner WHERE parent_id NOT IN (0)
-        # will return only the records with non-null parent_id.
-        #   SELECT id FROM res_partner WHERE parent_id IN (0)
-        # will return expectedly nothing (our ids always begin at 1).
-        # This means the union of those two results will give only some
-        # records, but not all present in database.
-        #
-        # When using domains and the ORM's search method, we think it is
-        # more intuitive that the union returns all the records, and that
-        # a domain like ('parent_id', 'not in', [0]) will return all
-        # the records. For instance, if you perform a search for the companies
-        # that don't have OpenERP has a parent company, you expect to find,
-        # among others, the companies that don't have parent company.
-        #
-       
-        # existing values be treated similarly if we simply check that some
-        # existing value belongs to them.
-
-        res_0 = self.search(cr, uid, [('parent_id', 'not like', 'probably_unexisting_name')]) # get all rows, included null parent_id
-        res_0.sort()
-        res_1 = self.search(cr, uid, [('parent_id', 'not in', [max_partner_id + 1])]) # get all rows, included null parent_id
-        res_1.sort()
-        res_2 = self.search(cr, uid, [('parent_id', '!=', False)]) # get rows with not null parent_id, deprecated syntax
-        res_2.sort()
-        res_3 = self.search(cr, uid, [('parent_id', 'not in', [])]) # get all rows, included null parent_id
-        res_3.sort()
-        res_4 = self.search(cr, uid, [('parent_id', 'not in', [False])]) # get rows with not null parent_id
-        res_4.sort()
-        res_4b = self.search(cr, uid, [('parent_id', 'not ilike', '')]) # get only rows without parent
-        res_4b.sort()
-        assert res_0 == partner_ids, 'res0: expected %r, got %r' % (partner_ids, res_0)
-        assert res_1 == partner_ids, 'res1: expected %r, got %r' % (partner_ids, res_1)
-        assert res_2 == with_parent, 'res2: expected %r, got %r' % (with_parent, res_2)
-        assert res_3 == partner_ids, 'res3: expected %r, got %r' % (partner_ids, res_3)
-        assert res_4 == with_parent, 'res4: expected %r, got %r' % (with_parent, res_4)
-        assert res_4b == without_parent, 'res4b: expected %r, got %r' % (without_parent, res_4b)
-        # The results of these queries, when combined with queries 0..4 must
-        # give the whole set of ids.
-        res_5 = self.search(cr, uid, [('parent_id', 'like', 'probably_unexisting_name')])
-        res_5.sort()
-        res_6 = self.search(cr, uid, [('parent_id', 'in', [max_partner_id + 1])])
-        res_6.sort()
-        res_7 = self.search(cr, uid, [('parent_id', '=', False)])
-        res_7.sort()
-        res_8 = self.search(cr, uid, [('parent_id', 'in', [])])
-        res_8.sort()
-        res_9 = self.search(cr, uid, [('parent_id', 'in', [False])])
-        res_9.sort()
-        res_9b = self.search(cr, uid, [('parent_id', 'ilike', '')]) # get those with a parent
-        res_9b.sort()
-
-        assert res_5 == [], 'res5: expected %r, got %r' % ([], res_5)
-        assert res_6 == [], 'res6: expected %r, got %r' % ([], res_6)
-        assert res_7 == without_parent, 'res7: expected %r, got %r' % (without_parent, res_7)
-        assert res_8 == [], 'res8: expected %r, got %r' % ([], res_8)
-        assert res_9 == without_parent, 'res9: expected %r, got %r' % (without_parent, res_9)
-        assert res_9b == with_parent, 'res9b: expected %r, got %r' % (with_parent, res_9b)
-        # These queries must return exactly the results than the queries 0..4,
-        # i.e. not ... in ... must be the same as ... not in ... .
-        res_10 = self.search(cr, uid, ['!', ('parent_id', 'like', 'probably_unexisting_name')])
-        res_10.sort()
-        res_11 = self.search(cr, uid, ['!', ('parent_id', 'in', [max_partner_id + 1])])
-        res_11.sort()
-        res_12 = self.search(cr, uid, ['!', ('parent_id', '=', False)])
-        res_12.sort()
-        res_13 = self.search(cr, uid, ['!', ('parent_id', 'in', [])])
-        res_13.sort()
-        res_14 = self.search(cr, uid, ['!', ('parent_id', 'in', [False])])
-        res_14.sort()
-        assert res_0 == res_10
-        assert res_1 == res_11
-        assert res_2 == res_12
-        assert res_3 == res_13
-        assert res_4 == res_14
-
-        # Testing many2one field is not enough, a regular char field is tested
-        # with in [] and must not return any result.
-        res_15 = self.search(cr, uid, [('website', 'in', [])])
-        assert res_15 == []
-        # not in [] must return everything.
-        res_16 = self.search(cr, uid, [('website', 'not in', [])])
-        res_16.sort()
-        assert res_16 == partner_ids
-
-        res_17 = self.search(cr, uid, [('website', '!=', False)])
-        res_17.sort()
-        assert res_17 == with_website
-
--
-    Check behavior for required many2one fields
--
-    !python {model: res.company }: |
-        company_ids = sorted(self.search(cr, uid, []))
-        # currency_id is required
-        res_101 = sorted(self.search(cr, uid, [('currency_id', 'not ilike', '')])) # get no companies
-        res_102 = sorted(self.search(cr, uid, [('currency_id', 'ilike', '')])) # get all companies
-        assert res_101 == [], 'res_101: expected %r, got %r' % ([], res_101)
-        assert res_102 == company_ids, 'res_102: expected %r, got %r' % (company_ids, res_102)
--
-    Verify domain evaluation for `one2many != False`
--
-    !python {model: res.partner.category }: |
-        all_ids = self.search(cr, uid, [])
-        parent_categs = set([c.parent_id.id for c in self.browse(cr, uid, all_ids) if c.parent_id])
-        result = set(self.search(cr, uid, [('child_ids', '!=', False)]))
-        assert result and result == parent_categs, "Got %r, expected %r" % (result, parent_categs) 
--
-    Verify domain evaluation for `one2many == False`
--
-    !python {model: res.partner.category }: |
-        all_ids = self.search(cr, uid, [])
-        parent_categs = set([c.parent_id.id for c in self.browse(cr, uid, all_ids) if c.parent_id])
-        leaf_categs = set(all_ids) - parent_categs
-        result = set(self.search(cr, uid, [('child_ids', '=', False)]))
-        assert result and result == leaf_categs, "Got %r, expected %r" % (result, leaf_categs)
--
-    Equivalent queries.
--
-    !python {model: res.currency }: |
-        max_currency_id = max(self.search(cr, uid, []))
-        res_0 = self.search(cr, uid, [])
-        res_1 = self.search(cr, uid, [('name', 'not like', 'probably_unexisting_name')])
-        res_2 = self.search(cr, uid, [('id', 'not in', [max_currency_id + 1003])])
-        res_3 = self.search(cr, uid, [('id', 'not in', [])])
-        res_4 = self.search(cr, uid, [('id', '!=', False)])
-        res_0.sort()
-        res_1.sort()
-        res_2.sort()
-        res_3.sort()
-        res_4.sort()
-        assert res_0 == res_1
-        assert res_0 == res_2
-        assert res_0 == res_3
-        assert res_0 == res_4
--
-    Equivalent queries, integer and string.
--
-    !python {model: res.partner }: |
-        all_ids = self.search(cr, uid, [])
-        if len(all_ids) > 1:
-            one = all_ids[0]
-            record = self.browse(cr, uid, one)
-            others = all_ids[1:]
-            res_1 = self.search(cr, uid, [('id', '=', one)])
-            # self.search(cr, uid, [('id', '!=', others)]) # not permitted
-            res_2 = self.search(cr, uid, [('id', 'not in', others)])
-            res_3 = self.search(cr, uid, ['!', ('id', '!=', one)])
-            res_4 = self.search(cr, uid, ['!', ('id', 'in', others)])
-            # res_5 = self.search(cr, uid, [('id', 'in', one)]) # TODO make it permitted, just like for child_of
-            res_6 = self.search(cr, uid, [('id', 'in', [one])])
-            res_7 = self.search(cr, uid, [('name', '=', record.name)])
-            res_8 = self.search(cr, uid, [('name', 'in', [record.name])])
-            # res_9 = self.search(cr, uid, [('name', 'in', record.name)]) # TODO
-            assert [one] == res_1
-            assert [one] == res_2
-            assert [one] == res_3
-            assert [one] == res_4
-            #assert [one] == res_5
-            assert [one] == res_6
-            assert [one] == res_7
--
-    Need a company with a parent_id.
--
-    !record {model: res.company, id: ymltest_company3}:
-        name: Acme 3
--
-    Need a company with a parent_id.
--
-    !record {model: res.company, id: ymltest_company4}:
-        name: Acme 4
-        parent_id: ymltest_company3
--
-    Equivalent queries, one2many.
--
-    !python {model: res.company }: |
-        # Search the company via its one2many (the one2many must point back at the company).
-        company = self.browse(cr, uid, ref('ymltest_company3'))
-        max_currency_id = max(self.pool.get('res.currency').search(cr, uid, []))
-        currency_ids1 = self.pool.get('res.currency').search(cr, uid, [('name', 'not like', 'probably_unexisting_name')])
-        currency_ids2 = self.pool.get('res.currency').search(cr, uid, [('id', 'not in', [max_currency_id + 1003])])
-        currency_ids3 = self.pool.get('res.currency').search(cr, uid, [('id', 'not in', [])])
-        assert currency_ids1 == currency_ids2 == currency_ids3, 'All 3 results should have be the same: all currencies'
-        default_company = self.browse(cr, uid, 1)
-
-        # Due to currency data definition change (in relation with bug 1111298), this test now needs
-        # a manual setup where all currencies are assigned to the default company.
-        self.pool['res.currency'].write(cr, uid, currency_ids1, {'company_id': default_company.id})
-
-        # one2many towards same model
-        res_1 = self.search(cr, uid, [('child_ids', 'in', [x.id for x in company.child_ids])]) # any company having a child of company3 as child
-        res_2 = self.search(cr, uid, [('child_ids', 'in', [company.child_ids[0].id])]) # any company having the first child of company3 as child
-        # one2many towards another model
-        res_3 = self.search(cr, uid, [('currency_ids', 'in', [x.id for x in default_company.currency_ids])]) # companies having a currency of main company
-        res_4 = self.search(cr, uid, [('currency_ids', 'in', [default_company.currency_ids[0].id])]) # companies having first currency of main company
-        res_5 = self.search(cr, uid, [('currency_ids', 'in', default_company.currency_ids[0].id)]) # companies having first currency of main company
-        # res_6 = self.search(cr, uid, [('currency_ids', 'in', [default_company.currency_ids[0].name])]) # TODO
-        res_7 = self.search(cr, uid, [('currency_ids', '=', default_company.currency_ids[0].name)])
-        res_8 = self.search(cr, uid, [('currency_ids', 'like', default_company.currency_ids[0].name)])
-        res_9 = self.search(cr, uid, [('currency_ids', 'like', 'probably_unexisting_name')])
-        # self.search(cr, uid, [('currency_ids', 'unexisting_op', 'probably_unexisting_name')]) # TODO expected exception
-        assert res_1 == [ref('ymltest_company3')]
-        assert res_2 == [ref('ymltest_company3')]
-        assert res_3 == [1]
-        assert res_4 == [1]
-        assert res_5 == [1]
-        assert res_7 == [1]
-        assert res_8 == [1]
-        assert res_9 == []
-
-        # get the companies referenced by some currency (this is normally the main company) using a weird negative domain
-        res_10 = self.search(cr, uid, [('currency_ids', 'not like', 'probably_unexisting_name')])
-        res_11 = self.search(cr, uid, [('currency_ids', 'not in', [max_currency_id + 1])])
-        res_12 = self.search(cr, uid, [('currency_ids', '!=', False)])
-        res_13 = self.search(cr, uid, [('currency_ids', 'not in', [])])
-        res_10.sort()
-        res_11.sort()
-        res_12.sort()
-        res_13.sort()
-        assert res_10 == res_11
-        assert res_10 == res_12
-        assert res_10 == res_13
-        
-        # try testing real subsets with IN/NOT IN
-        res_partner = self.pool.get('res.partner')
-        res_users = self.pool.get('res.users')
-        p1, _ = res_partner.name_create(cr, uid, "Dédé Boitaclou")
-        p2, _ = res_partner.name_create(cr, uid, "Raoulette Pizza O'poil")
-        u1a = res_users.create(cr, uid, {'login': 'dbo', 'partner_id': p1})
-        u1b = res_users.create(cr, uid, {'login': 'dbo2', 'partner_id': p1})
-        u2 = res_users.create(cr, uid, {'login': 'rpo', 'partner_id': p2})
-        assert [p1] == res_partner.search(cr, uid, [('user_ids', 'in', u1a)]), "o2m IN accept single int on right side"
-        assert [p1,p2] == res_partner.search(cr, uid, [('user_ids', 'in', [u1a,u2])]), "o2m IN matches any on the right side"
-        all_partners = res_partner.search(cr, uid, [])
-        assert (set(all_partners) - set([p1])) == set(res_partner.search(cr, uid, [('user_ids', 'not in', u1a)])), "o2m NOT IN matches none on the right side"
-        assert (set(all_partners) - set([p1,p2])) == set(res_partner.search(cr, uid, [('user_ids', 'not in', [u1b, u2])])), "o2m NOT IN matches none on the right side"
-
-        # child_of x returns x and its children (direct or not).
-        company = self.browse(cr, uid, ref('ymltest_company3'))
-        expected = [ref('ymltest_company3'), ref('ymltest_company4')]
-        expected.sort()
-        res_1 = self.search(cr, uid, [('id', 'child_of', [ref('ymltest_company3')])])
-        res_1.sort()
-        res_2 = self.search(cr, uid, [('id', 'child_of', ref('ymltest_company3'))])
-        res_2.sort()
-        res_3 = self.search(cr, uid, [('id', 'child_of', [company.name])])
-        res_3.sort()
-        res_4 = self.search(cr, uid, [('id', 'child_of', company.name)])
-        res_4.sort()
-        assert res_1 == expected
-        assert res_2 == expected
-        assert res_3 == expected
-        assert res_4 == expected
--
-    Unaccent. Create a company with an accent in its name.
--
-    !record {model: res.company, id: ymltest_unaccent_company}:
-        name: Hélène
--
-    Test the unaccent-enabled 'ilike'.
--
-    !python {model: res.company}: |
-        if self.pool.has_unaccent:
-            ids = self.search(cr, uid, [('name','ilike','Helene')], {})
-            assert ids == [ref('ymltest_unaccent_company')]
-            ids = self.search(cr, uid, [('name','ilike','hélène')], {})
-            assert ids == [ref('ymltest_unaccent_company')]
-            ids = self.search(cr, uid, [('name','not ilike','Helene')], {})
-            assert ref('ymltest_unaccent_company') not in ids
-            ids = self.search(cr, uid, [('name','not ilike','hélène')], {})
-            assert ref('ymltest_unaccent_company') not in ids
--
-    Check that =like/=ilike expressions (no wildcard variants of like/ilike) are working on an untranslated field.
--
-    !python {model: res.partner }: |
-        all_ids = self.search(cr, uid, [('name', '=like', 'A_e_or')])
-        assert len(all_ids) == 1, "Must match one partner (Axelor), got %r"%all_ids
-        all_ids = self.search(cr, uid, [('name', '=ilike', 'v%')])
-        assert len(all_ids) >= 1, "Must match one partner (Vicking Direct), got %r"%all_ids
--
-    Check that =like/=ilike expressions (no wildcard variants of like/ilike) are working on translated field.
--
-    !python {model: res.country }: |
-        all_ids = self.search(cr, uid, [('name', '=like', 'Ind__')])
-        assert len(all_ids) == 1, "Must match India only, got %r"%all_ids
-        all_ids = self.search(cr, uid, [('name', '=ilike', 'z%')])
-        assert len(all_ids) == 3, "Must match only countries with names starting with Z (currently 3), got %r"%all_ids
--
-    Use the create_date column on res.country (which doesn't declare it in _columns).
--
-    !python {model: res.country }: |
-        ids = self.search(cr, uid, [('create_date', '<', '2001-01-01 12:00:00')])
-
-
--
-    Verify that invalid expressions are refused, even for magic fields
--
-    !python {model: res.country }: |
-        try:
-            self.search(cr, uid, [('does_not_exist', '=', 'foo')])
-            raise AssertionError('Invalid fields should not be accepted')
-        except ValueError:
-            pass
-
-        try:
-            self.search(cr, uid, [('create_date', '>>', 'foo')])
-            raise AssertionError('Invalid operators should not be accepted')
-        except ValueError:
-            pass
-
-        import psycopg2
-        try:
-            cr._default_log_exceptions = False
-            cr.execute('SAVEPOINT expression_failure_test')
-            self.search(cr, uid, [('create_date', '=', "1970-01-01'); --")])
-            # if the above search gives no error, the operand was not escaped!
-            cr.execute('RELEASE SAVEPOINT expression_failure_test')
-            raise AssertionError('Operands should always be SQL escaped')
-        except psycopg2.DataError:
-            # Should give: 'DataError: invalid input syntax for type timestamp' or similar
-            cr.execute('ROLLBACK TO SAVEPOINT expression_failure_test')
--
-    Testing for Many2Many field with category supplier and active=False
--
-    !python {model: res.partner }: |
-        vals = {'category_id': [(6, 0, [ref("base.res_partner_category_1")])],
-                'name': 'OpenERP Test',
-                'active': False,
-                'child_ids': [(0, 0, {'name': 'address of OpenERP Test', 'country_id': ref("base.be")})]
-                }
-        self.create(cr, uid, vals, context=context)
-        res_ids = self.search(cr, uid, [('category_id', 'ilike', 'supplier'), ('active', '=', False)])
-        assert len(res_ids) != 0, "Record not Found with category supplier and active False."
--
-    Testing for One2Many field with country Belgium and active=False
--
-    !python {model: res.partner }: |
-        res_ids = self.search(cr, uid, [('child_ids.country_id','=','Belgium'),('active','=',False)])
-        assert len(res_ids) != 0, "Record not Found with country Belgium and active False."
--
-    Check that we can exclude translated fields (bug lp:1071710)
--
-    !python {model: res.country}: |
-        # first install french language
-        Modules = self.pool.get('ir.module.module')
-        base = Modules.browse(cr, uid, Modules.search(cr, uid, [('name', '=', 'base')])[0])
-        
-        # hack: mark module as installed to allow install of the translation
-        base_state = base.state
-        base.write({'state': 'installed'})
-        base.update_translations('fr_FR')
-        base.write({'state': base_state})
-
-        # tests
-        be_id = self.search(cr, uid, [('name', '=', 'Belgium')])[0]
-
-        ctx = {'lang': 'fr_FR'}
-        not_be = self.search(cr, uid, [('name', '!=', 'Belgique')], context=ctx)
-
-        assert be_id not in not_be, "Search match failed"
-
-        # indirect search via m2o
-        Partners = self.pool.get('res.partner')
-        agrolait = Partners.search(cr, uid, [('name', '=', 'Agrolait')])[0]
-
-        not_be = Partners.search(cr, uid, [('country_id', '!=', 'Belgium')])
-        assert agrolait not in not_be, "Search match failed (m2o)"
-
-        not_be = Partners.search(cr, uid, [('country_id', '!=', 'Belgique')], context=ctx)
-        assert agrolait not in not_be, "Search match failed (m2o)"
diff --git a/openerp/addons/base/tests/base_test.yml b/openerp/addons/base/tests/base_test.yml
new file mode 100644 (file)
index 0000000..e73d817
--- /dev/null
@@ -0,0 +1,318 @@
+-   |
+    To check that common dangerous operations are not allowed by the safe_eval mechanism, attempt to 
+    evaluate unauthorized expressions, and verify that they trigger an error.
+-
+    1. Try a few common expressions to verify they work with safe_eval
+-
+    !python {model: ir.model}: |
+        from openerp.tools.safe_eval import safe_eval
+        expected = (1, {"a": 9 * 2}, (True, False, None))
+        actual = safe_eval('(1, {"a": 9 * 2}, (True, False, None))')
+        assert actual == expected, "Simple python expressions are not working with safe_eval"
+-
+    2. Try simple literal definition to verify it works with literal_eval
+-
+    !python {model: ir.model}: |
+        import ast
+        expected = (1, {"a": 9}, (True, False, None))
+        actual = ast.literal_eval('(1, {"a": 9}, (True, False, None))')
+        assert actual == expected, "Simple python expressions are not working with literal_eval"
+-
+    3. Try arithmetic expression in literal_eval to verify it does not work
+-
+    !python {model: ir.model}: |
+        import ast
+        try:
+           ast.literal_eval('(1, {"a": 2*9}, (True, False, None))')
+           assert False, "literal_eval should not accept arithmetic expressions"
+        except ValueError:
+           pass
+-
+    4. Try forbidden expressions in literal_eval to verify they are not allowed
+-
+    !python {model: ir.model}: |
+        import ast
+        try:
+           ast.literal_eval('{"a": True.__class__}')
+           assert False, "literal_eval should accept only literals"
+        except ValueError:
+           pass
+-
+    5. Try forbidden expressions in safe_eval to verify they are not allowed (open)
+-
+    !python {model: ir.model}: |
+        from openerp.tools.safe_eval import safe_eval
+        from openerp.tools.misc import mute_logger
+        try:
+            with mute_logger('openerp.tools.safe_eval'):
+                safe_eval('open("/etc/passwd","r")')
+            assert False, "safe_eval should not allow calling open() builtin"
+        except ValueError:
+            pass
+
+-
+    "ORM test: verify that parent_store computation are going right"
+-
+    0. Emulate normal behavior of tree structure storing
+-
+    !python {model: res.partner.category}: |
+        # pretend the pool has finished loading to avoid deferring parent_store computation
+        self.pool._init = False
+-
+    "1.0 Setup test partner categories: parent root"
+-
+    !record {model: res.partner.category, id: test_categ_root}:
+        name: Root category
+-
+    "1.1 Setup test partner categories: parent category"
+-
+    !record {model: res.partner.category, id: test_categ_0}:
+        name: Parent category
+        parent_id: test_categ_root
+-
+    "1.2 Setup test partner categories: child 1"
+-
+    !record {model: res.partner.category, id: test_categ_1}:
+        name: Child 1
+        parent_id: test_categ_0
+-
+    "1.3 Setup test partner categories: child 2"
+-
+    !record {model: res.partner.category, id: test_categ_2}:
+        name: Child 2
+        parent_id: test_categ_0
+-
+    "1.4 Setup test partner categories: child 2-1"
+-
+    !record {model: res.partner.category, id: test_categ_21}:
+        name: Child 2-1
+        parent_id: test_categ_2
+-
+    2. Duplicate the parent category and verify that the children have been duplicated too and are below the new parent
+-
+    !python {model: res.partner.category}: |
+        new_id = self.copy(cr, uid, ref('test_categ_0'))
+        new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
+        assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
+        old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
+        assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
+        assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
+-
+    3. Duplicate the children then reassign them to the new parent (1st method) and check the parent_store structure.
+-
+    !python {model: res.partner.category}: |
+        new_child1_id = self.copy(cr, uid, ref('test_categ_1'))
+        new_child2_id = self.copy(cr, uid, ref('test_categ_2'))
+        new_id = self.copy(cr, uid, ref('test_categ_0'), {'child_ids': []})
+        self.write(cr, uid, [new_child1_id, new_child2_id], {'parent_id': new_id})
+        new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
+        assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
+        old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
+        assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
+        assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
+-
+    4. Duplicate the children then reassign them to the new parent (2nd method) and check the parent_store structure.
+-
+    !python {model: res.partner.category}: |
+        new_child1_id = self.copy(cr, uid, ref('test_categ_1'))
+        new_child2_id = self.copy(cr, uid, ref('test_categ_2'))
+        old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
+        new_id = self.copy(cr, uid, ref('test_categ_0'), {'child_ids': [(6,0,[new_child1_id, new_child2_id])]})
+        new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
+        assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
+        old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
+        assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
+        assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
+-
+    5. Duplicate the children then reassign them to the new parent (3rd method) and make sure the parent_store structure is still right.
+-
+    !python {model: res.partner.category}: |
+        new_child1_id = self.copy(cr, uid, ref('test_categ_1'))
+        new_child2_id = self.copy(cr, uid, ref('test_categ_2'))
+        new_id = self.copy(cr, uid, ref('test_categ_0'), {'child_ids': []})
+        self.write(cr, uid, [new_id], {'child_ids': [(4,new_child1_id), (4,new_child2_id)]})
+        new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
+        assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
+        old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
+        assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
+        assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
+-
+    6. Restore pool state after the test
+-
+    !python {model: res.partner.category}: |
+        self.pool._init = True
+
+-
+    "Float precision tests: verify that float rounding methods are working correctly via res.currency"
+-
+    !python {model: res.currency}: |
+        from openerp.tools import float_repr
+        from math import log10
+        currency = self.browse(cr, uid, ref('base.EUR'))
+        def try_round(amount, expected, self=self, cr=cr, currency=currency, float_repr=float_repr,
+                      log10=log10):
+            digits = max(0,-int(log10(currency.rounding)))
+            result = float_repr(self.round(cr, 1, currency, amount), precision_digits=digits)
+            assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
+        try_round(2.674,'2.67')
+        try_round(2.675,'2.68')   # in Python 2.7.2, round(2.675,2) gives 2.67
+        try_round(-2.675,'-2.68') # in Python 2.7.2, round(2.675,2) gives 2.67
+        try_round(0.001,'0.00')
+        try_round(-0.001,'-0.00')
+        try_round(0.0049,'0.00')   # 0.0049 is closer to 0 than to 0.01, so should round down
+        try_round(0.005,'0.01')   # the rule is to round half away from zero
+        try_round(-0.005,'-0.01') # the rule is to round half away from zero
+
+        def try_zero(amount, expected, self=self, cr=cr, currency=currency):
+            assert self.is_zero(cr, 1, currency, amount) == expected, "Rounding error: %s should be zero!" % amount
+        try_zero(0.01, False)
+        try_zero(-0.01, False)
+        try_zero(0.001, True)
+        try_zero(-0.001, True)
+        try_zero(0.0046, True)
+        try_zero(-0.0046, True)
+        try_zero(2.68-2.675, False) # 2.68 - 2.675 = 0.005 -> rounds to 0.01
+        try_zero(2.68-2.676, True)  # 2.68 - 2.675 = 0.004 -> rounds to 0.0
+        try_zero(2.676-2.68, True)  # 2.675 - 2.68 = -0.004 -> rounds to -0.0
+        try_zero(2.675-2.68, False) # 2.675 - 2.68 = -0.005 -> rounds to -0.01
+
+        def try_compare(amount1, amount2, expected, self=self, cr=cr, currency=currency):
+            assert self.compare_amounts(cr, 1, currency, amount1, amount2) == expected, \
+                "Rounding error, compare_amounts(%s,%s) should be %s" % (amount1, amount2, expected)
+        try_compare(0.001, 0.001, 0)
+        try_compare(-0.001, -0.001, 0)
+        try_compare(0.001, 0.002, 0)
+        try_compare(-0.001, -0.002, 0)
+        try_compare(2.675, 2.68, 0)
+        try_compare(2.676, 2.68, 0)
+        try_compare(-2.676, -2.68, 0)
+        try_compare(2.674, 2.68, -1)
+        try_compare(-2.674, -2.68, 1)
+        try_compare(3, 2.68, 1)
+        try_compare(-3, -2.68, -1)
+        try_compare(0.01, 0, 1)
+        try_compare(-0.01, 0, -1)
+
+-
+    "Float precision tests: verify that float rounding methods are working correctly via tools"
+-
+    !python {model: res.currency}: |
+        from openerp.tools import float_compare, float_is_zero, float_round, float_repr
+        def try_round(amount, expected, precision_digits=3, float_round=float_round, float_repr=float_repr):
+            result = float_repr(float_round(amount, precision_digits=precision_digits),
+                                precision_digits=precision_digits)
+            assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
+        try_round(2.6745, '2.675')
+        try_round(-2.6745, '-2.675')
+        try_round(2.6744, '2.674')
+        try_round(-2.6744, '-2.674')
+        try_round(0.0004, '0.000')
+        try_round(-0.0004, '-0.000')
+        try_round(357.4555, '357.456')
+        try_round(-357.4555, '-357.456')
+        try_round(457.4554, '457.455')
+        try_round(-457.4554, '-457.455')
+
+        # Extended float range test, inspired by Cloves Almeida's test on bug #882036.
+        fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555]
+        expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556']
+        precisions = [2, 2, 2, 2, 2, 2, 3, 4]
+        # Note: max precision for double floats is 53 bits of precision or
+        # 17 significant decimal digits
+        for magnitude in range(7):
+            for i in xrange(len(fractions)):
+                frac, exp, prec = fractions[i], expecteds[i], precisions[i]
+                for sign in [-1,1]:
+                    for x in xrange(0,10000,97):
+                        n = x * 10**magnitude
+                        f = sign * (n + frac)
+                        f_exp = ('-' if f != 0 and sign == -1 else '') + str(n) + exp 
+                        try_round(f, f_exp, precision_digits=prec)
+
+
+        def try_zero(amount, expected, float_is_zero=float_is_zero):
+            assert float_is_zero(amount, precision_digits=3) == expected, "Rounding error: %s should be zero!" % amount
+        try_zero(0.0002, True)
+        try_zero(-0.0002, True)
+        try_zero(0.00034, True)
+        try_zero(0.0005, False)
+        try_zero(-0.0005, False)
+        try_zero(0.0008, False)
+        try_zero(-0.0008, False)
+
+        def try_compare(amount1, amount2, expected, float_compare=float_compare):
+            assert float_compare(amount1, amount2, precision_digits=3) == expected, \
+                "Rounding error, compare_amounts(%s,%s) should be %s" % (amount1, amount2, expected)
+        try_compare(0.0003, 0.0004, 0)
+        try_compare(-0.0003, -0.0004, 0)
+        try_compare(0.0002, 0.0005, -1)
+        try_compare(-0.0002, -0.0005, 1)
+        try_compare(0.0009, 0.0004, 1)
+        try_compare(-0.0009, -0.0004, -1)
+        try_compare(557.4555, 557.4556, 0)
+        try_compare(-557.4555, -557.4556, 0)
+        try_compare(657.4444, 657.445, -1)
+        try_compare(-657.4444, -657.445, 1)
+
+        # Rounding to unusual rounding units (e.g. coin values)
+        def try_round(amount, expected, precision_rounding=None, float_round=float_round, float_repr=float_repr):
+            result = float_repr(float_round(amount, precision_rounding=precision_rounding),
+                                precision_digits=2)
+            assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
+        try_round(-457.4554, '-457.45', precision_rounding=0.05)
+        try_round(457.444, '457.50', precision_rounding=0.5)
+        try_round(457.3, '455.00', precision_rounding=5)
+        try_round(457.5, '460.00', precision_rounding=5)
+        try_round(457.1, '456.00', precision_rounding=3)
+
+-
+    "Float precision tests: check that proper rounding is performed for float persistence"
+-
+    !python {model: res.currency}: |
+        currency = self.browse(cr, uid, ref('base.EUR'))
+        res_currency_rate = self.pool.get('res.currency.rate')
+        from openerp.tools import float_compare, float_is_zero, float_round, float_repr
+        def try_roundtrip(value, expected, self=self, cr=cr, currency=currency,
+                          res_currency_rate=res_currency_rate):
+            rate_id = res_currency_rate.create(cr, 1, {'name':'2000-01-01',
+                                                       'rate': value,
+                                                       'currency_id': currency.id})
+            rate = res_currency_rate.read(cr, 1, rate_id, ['rate'])['rate']
+            assert rate == expected, 'Roundtrip error: got %s back from db, expected %s' % (rate, expected)
+        # res.currency.rate uses 6 digits of precision by default
+        try_roundtrip(2.6748955, 2.674896)
+        try_roundtrip(-2.6748955, -2.674896)
+        try_roundtrip(10000.999999, 10000.999999)
+        try_roundtrip(-10000.999999, -10000.999999)
+
+-
+    "Float precision tests: verify that invalid parameters are forbidden"
+-
+    !python {model: res.currency}: |
+        from openerp.tools import float_compare, float_is_zero, float_round
+        try:
+            float_is_zero(0.01, precision_digits=3, precision_rounding=0.01)
+        except AssertionError:
+            pass
+        try:
+            float_compare(0.01, 0.02, precision_digits=3, precision_rounding=0.01)
+        except AssertionError:
+            pass
+        try:
+            float_round(0.01, precision_digits=3, precision_rounding=0.01)
+        except AssertionError:
+            pass
+-
+   Test res.groups name search
+-
+     !python {model: res.groups}: |
+         all_groups = self.search(cr, uid, [])
+         full_names = [(group.id, group.full_name) for group in self.browse(cr, uid, all_groups)]
+         group_ids = self.search(cr, uid, [('full_name', 'like', '%Sale%')])
+         assert set(group_ids) == set([id for (id, full_name) in full_names if 'Sale' in full_name]), "did not match search for 'Sale'"
+         group_ids = self.search(cr, uid, [('full_name', 'like', '%Technical%')])
+         assert set(group_ids) == set([id for (id, full_name) in full_names if 'Technical' in full_name]), "did not match search for 'Technical'"
+         group_ids = self.search(cr, uid, [('full_name', 'like', '%Sales /%')])
+         assert set(group_ids) == set([id for (id, full_name) in full_names if 'Sales /' in full_name]), "did not match search for 'Sales /'"
+         group_ids = self.search(cr, uid, [('full_name', 'in', ['Administration / Access Rights','Contact Creation'])])
+         assert group_ids, "did not match search for 'Administration / Access Rights' and 'Contact Creation'"
diff --git a/openerp/addons/base/tests/test_ir_rule.yml b/openerp/addons/base/tests/test_ir_rule.yml
new file mode 100644 (file)
index 0000000..49b368a
--- /dev/null
@@ -0,0 +1,189 @@
+-
+    Exercise ir_rule.py code (and indirectly expression.py).
+-
+    Create an ir_rule for the Employee group (called base.group_user)
+    with an blank domain.
+-
+    !record {model: ir.rule, id: test_rule}:
+        model_id: base.model_res_partner
+        domain_force: False
+        name: test_rule
+        groups:
+            - base.group_user
+        perm_unlink: 1
+        perm_write: 1
+        perm_read: 1
+        perm_create: 1
+-
+    Read as demo user the partners (one blank domain).
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see some partner."
+-
+    Domain is not empty.
+-
+    !record {model: ir.rule, id: test_rule}:
+        model_id: base.model_res_partner
+        domain_force: "[(1,'=',1)]"
+-
+    Read as demo user the partners (one 1=1 domain).
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see some partner."
+-
+    Domain is an empty list.
+-
+    !record {model: ir.rule, id: test_rule}:
+        model_id: base.model_res_partner
+        domain_force: "[]"
+-
+    Read as demo user the partners (one [] domain).
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see some partner."
+-
+    Create another ir_rule for the Employee group (to test rules from
+    multiple groups).
+-
+    !record {model: ir.rule, id: test_rule2}:
+        model_id: base.model_res_partner
+        domain_force: False
+        name: test_rule2
+        groups:
+            - base.group_user
+        perm_unlink: 1
+        perm_write: 1
+        perm_read: 1
+        perm_create: 1
+-
+    Read as demo user the partners (blank and [] domains).
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see some partner."
+-
+    Domain is not empty.
+-
+    !record {model: ir.rule, id: test_rule}:
+        model_id: base.model_res_partner
+        domain_force: "[(1,'=',1)]"
+-
+    Read as demo user the partners (1=1 and blank domain).
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see some partner."
+-
+    Domain is not empty.
+-
+    !record {model: ir.rule, id: test_rule2}:
+        model_id: base.model_res_partner
+        domain_force: "[(1,'=',1)]"
+-
+    Read as demo user the partners (two 1=1 domains).
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see some partner."
+-
+    Create another ir_rule for the Employee group (to test rules from
+    multiple groups).
+-
+    !record {model: ir.rule, id: test_rule3}:
+        model_id: base.model_res_partner
+        domain_force: False
+        name: test_rule3
+        groups:
+            - base.group_user
+        perm_unlink: 1
+        perm_write: 1
+        perm_read: 1
+        perm_create: 1
+-
+    Read as demo user the partners.
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see some partner."
+-
+    Domain is not empty.
+-
+    !record {model: ir.rule, id: test_rule3}:
+        model_id: base.model_res_partner
+        domain_force: "[(1,'=',1)]"
+-
+    Read as demo user the partners (three 1=1 domains).
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see some partner."
+-
+    Modify the global rule on res_company which triggers a recursive check
+    of the rules on company.
+-
+    !record {model: ir.rule, id: base.res_company_rule}:
+        domain_force: "[('id','child_of',[user.company_id.id])]"
+-
+    Read as demo user the partners (exercising the global company rule).
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see some partner."
+-
+    Modify the ir_rule for employee to have a rule that fordids
+    seeing any record.
+    We use a domain with implicit AND operator for later tests
+    on normalization.
+-
+    !record {model: ir.rule, id: test_rule2}:
+        domain_force: "[('id','=',False),('name','=',False)]"
+-
+    Check that demo user still sees partners, because group-rules are OR'ed.
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see some partner."
+-
+    Create a new group with demo user in it, and a complex rule.
+    Demo should still see partners.
+-
+    !record {model: res.groups, id: test_group}:
+        name: Test Group
+        users:
+            - base.user_demo
+-
+    Add the rule to the new group, with a domain containing an implicit AND operator,
+    which is more tricky because it will have to be normalized before combining it.
+-
+    !record {model: ir.rule, id: test_rule3}:
+        model_id: base.model_res_partner
+        domain_force: "[('name', '!=', False),('id', '!=', False)]"
+        name: test_rule4
+        groups:
+            - test_group
+        perm_unlink: 1
+        perm_write: 1
+        perm_read: 1
+        perm_create: 1
+-
+    Read the partners again as demo user, which should give results.
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see partners even with the combined rules."
+-
+    Delete global domains (to combine only group domains).
+-
+    !python {model: ir.rule}: |
+        ids = self.search(cr, uid, [('groups','=',False)])
+        assert ids, "Demo user should see some partner."
+        self.unlink(cr, uid, ids)
+-
+    Read as demo user the partners (several group domains, no global domain).
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, ref('base.user_demo'), [])
+        assert ids, "Demo user should see some partner."
diff --git a/openerp/addons/base/tests/test_osv_expression.yml b/openerp/addons/base/tests/test_osv_expression.yml
new file mode 100644 (file)
index 0000000..e006113
--- /dev/null
@@ -0,0 +1,563 @@
+-
+    Testing for hierarchical search in M2M
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, uid, [('category_id', 'child_of',ref('res_partner_category_0'))])
+        assert len(ids) >= 1, ids
+-
+    "1.0 Setup test partner categories: parent root"
+-
+    !record {model: res.partner.category, id: categ_root}:
+        name: Root category
+-
+    "1.1 Setup test partner categories: parent category"
+-
+    !record {model: res.partner.category, id: categ_0}:
+        name: Parent category
+        parent_id: categ_root
+-
+    "1.2 Setup test partner categories: child 1"
+-
+    !record {model: res.partner.category, id: categ_1}:
+        name: Child 1
+        parent_id: categ_0
+-
+    Test hierarchical search in M2M with child ID (list of ids)
+-
+    !python {model: res.partner.category }: |
+        ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_root')])])
+        assert len(ids) == 3, ids
+-
+    Test hierarchical search in M2M with child ID (single id)
+-
+    !python {model: res.partner.category }: |
+        ids = self.search(cr, uid, [('id', 'child_of',ref('categ_root'))])
+        assert len(ids) == 3, ids
+-
+    Test hierarchical search in M2M with child IDs
+-
+    !python {model: res.partner.category }: |
+        ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_1'), ref('categ_0')])])
+        assert len(ids) == 2, ids
+-
+    Test hierarchical search in M2M with child IDs
+-
+    !python {model: res.partner.category }: |
+        ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_0')])])
+        assert len(ids) == 2, ids
+-
+    Test hierarchical search in M2M with child IDs
+-
+    !python {model: res.partner.category }: |
+        ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_1')])])
+        assert len(ids) == 1, ids
+-
+    Testing that some domain expressions work
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, uid, [('parent_id','=','Agrolait')])
+        assert len(ids) >= 1, ids
+-
+    Trying the "in" operator, for scalar value
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, uid, [('parent_id','in','Agrolait')])
+        assert len(ids) >= 1, ids
+-
+    Trying the "in" operator for list value
+-
+    !python {model: res.partner }: |
+        ids = self.search(cr, uid, [('parent_id','in',['Agrolait','ASUStek'])])
+        assert len(ids) >= 1, ids
+-
+    Check we can use "in" operator for plain fields.
+-
+    !python {model: ir.ui.menu }: |
+        ids = self.search(cr, uid, [('sequence','in',[1, 2, 10, 20])])
+        assert len(ids) >= 1, ids
+-
+    Test one2many operator with empty search list
+-
+    !assert {model: res.partner, search: "[('child_ids', 'in', [])]", count: 0, string: "Ids should be empty"}
+-
+    Test one2many operator with False
+-
+    !assert {model: res.partner, search: "[('child_ids', '=', False)]"}:
+        - child_ids in (False, None, [])
+-
+    Test many2many operator with empty search list
+-
+    !assert {model: res.partner, search: "[('category_id', 'in', [])]", count: 0, string: "Ids should be empty"}
+-
+    Test many2many operator with False
+-
+    !assert {model: res.partner, search: "[('category_id', '=', False)]"}:
+        - category_id in (False, None, [])
+-
+    Filtering on invalid value across x2many relationship should return an empty set
+-
+    !assert {model: res.partner, search: "[('child_ids.city','=','foo')]", count: 0, string: "Searching for address.city = foo should give empty results"}
+-
+    Check if many2one works with empty search list
+-
+    !assert {model: res.partner, search: "[('company_id','in', [])]", count: 0, string: "Searching for company_id in [] should be empty!" }
+-
+    For the sake of the following tests, I will create a second company
+-
+    !record {model: res.company, id: ymltest_company2}:
+        name: Acme 2
+-
+    And create a few partners with that company or no company
+-
+    !python {model: res.partner }: |
+        for r in range(4):
+            self.create(cr, uid, { 'name': 'P of Acme %d' % r,
+                    'company_id': ref('ymltest_company2') })
+        for r in range(4):
+            self.create(cr, uid, { 'name': 'P of All %d' % r,
+                    'company_id': False })
+-
+    Check if many2one works with negative empty list
+-
+    !python {model: res.partner }: |
+        all_ids = self.search(cr, uid, [])
+        all_ids.sort()
+        res_ids = self.search(cr, uid,['|',('company_id','not in', []), ('company_id','=',False)])
+        res_ids.sort()
+        assert all_ids == res_ids, "not in [] fails"
+-
+    Check that many2one will pick the correct records with a list
+-
+    !python {model: res.partner }: |
+        res_ids = self.search(cr, uid, [('company_id', 'in', [False,])])
+        assert len(res_ids) >= 4, "We created 4 partners w/company, why find %d? %r" % \
+                    (len(res_ids), res_ids)
+-
+    Check that many2one will exclude the correct records with a list
+-
+    !python {model: res.partner }: |
+        # assuming that the default company is #1
+        res_ids = self.search(cr, uid, [('company_id', 'not in', [1])])
+        assert len(res_ids) >= 4, "We should have found 4 records at least, only have %d! %r" % \
+            (len(res_ids), res_ids)
+-
+    Check that we exclude the correct records, + False
+-
+    !python {model: res.partner }: |
+        # assuming that the default company is #1
+        res_ids = self.search(cr, uid, ['|', ('company_id', 'not in', [1]), ('company_id', '=', False)])
+        assert len(res_ids) >= 8, "We should have found 8 records at least, only have %d! %r" % \
+            (len(res_ids), res_ids)
+-
+    Check that multi-level expressions also work
+-
+    !python {model: res.partner }: |
+        res_ids = self.search(cr, uid, [('company_id.partner_id', 'in', [])])
+        assert res_ids == [], "Searching an empty set should return empty result, not %r" % res_ids
+-
+    Check that multi-level expressions with negative op work
+-
+    !python {model: res.partner }: |
+        all_ids = self.search(cr, uid, [('company_id', '!=', False)])
+        all_ids.sort()
+        res_ids = self.search(cr, uid, [('company_id.partner_id', 'not in', [])])
+        res_ids.sort()
+        assert res_ids == all_ids, "Searching against empty set failed, returns %r" % res_ids
+-
+    Test the '(not) like/in' behavior. res.partner and its parent_id column are used because
+    parent_id is a many2one, allowing to test the Null value, and there are actually some
+    null and non-null values in the demo data.
+-
+    !python {model: res.partner }: |
+        partner_ids = self.search(cr, uid, [])
+        partner_ids.sort()
+        max_partner_id = max(partner_ids)
+
+        # Grab test sample data without using a normal
+        # search domain, because we want to test these later,
+        # so we can't rely on them!
+        partners = self.browse(cr, uid, partner_ids)
+        with_parent = []
+        without_parent = []
+        with_website = []
+        for x in partners:
+            if x.parent_id:
+                with_parent.append(x.id)
+            else:
+                without_parent.append(x.id)
+            if x.website:
+                with_website.append(x.id)
+        with_parent.sort()
+        without_parent.sort()
+        with_website.sort()
+
+        # We treat null values differently than in SQL. For instance in SQL:
+        #   SELECT id FROM res_partner WHERE parent_id NOT IN (0)
+        # will return only the records with non-null parent_id.
+        #   SELECT id FROM res_partner WHERE parent_id IN (0)
+        # will return expectedly nothing (our ids always begin at 1).
+        # This means the union of those two results will give only some
+        # records, but not all present in database.
+        #
+        # When using domains and the ORM's search method, we think it is
+        # more intuitive that the union returns all the records, and that
+        # a domain like ('parent_id', 'not in', [0]) will return all
+        # the records. For instance, if you perform a search for the companies
+        # that don't have OpenERP has a parent company, you expect to find,
+        # among others, the companies that don't have parent company.
+        #
+       
+        # existing values be treated similarly if we simply check that some
+        # existing value belongs to them.
+
+        res_0 = self.search(cr, uid, [('parent_id', 'not like', 'probably_unexisting_name')]) # get all rows, included null parent_id
+        res_0.sort()
+        res_1 = self.search(cr, uid, [('parent_id', 'not in', [max_partner_id + 1])]) # get all rows, included null parent_id
+        res_1.sort()
+        res_2 = self.search(cr, uid, [('parent_id', '!=', False)]) # get rows with not null parent_id, deprecated syntax
+        res_2.sort()
+        res_3 = self.search(cr, uid, [('parent_id', 'not in', [])]) # get all rows, included null parent_id
+        res_3.sort()
+        res_4 = self.search(cr, uid, [('parent_id', 'not in', [False])]) # get rows with not null parent_id
+        res_4.sort()
+        res_4b = self.search(cr, uid, [('parent_id', 'not ilike', '')]) # get only rows without parent
+        res_4b.sort()
+        assert res_0 == partner_ids, 'res0: expected %r, got %r' % (partner_ids, res_0)
+        assert res_1 == partner_ids, 'res1: expected %r, got %r' % (partner_ids, res_1)
+        assert res_2 == with_parent, 'res2: expected %r, got %r' % (with_parent, res_2)
+        assert res_3 == partner_ids, 'res3: expected %r, got %r' % (partner_ids, res_3)
+        assert res_4 == with_parent, 'res4: expected %r, got %r' % (with_parent, res_4)
+        assert res_4b == without_parent, 'res4b: expected %r, got %r' % (without_parent, res_4b)
+        # The results of these queries, when combined with queries 0..4 must
+        # give the whole set of ids.
+        res_5 = self.search(cr, uid, [('parent_id', 'like', 'probably_unexisting_name')])
+        res_5.sort()
+        res_6 = self.search(cr, uid, [('parent_id', 'in', [max_partner_id + 1])])
+        res_6.sort()
+        res_7 = self.search(cr, uid, [('parent_id', '=', False)])
+        res_7.sort()
+        res_8 = self.search(cr, uid, [('parent_id', 'in', [])])
+        res_8.sort()
+        res_9 = self.search(cr, uid, [('parent_id', 'in', [False])])
+        res_9.sort()
+        res_9b = self.search(cr, uid, [('parent_id', 'ilike', '')]) # get those with a parent
+        res_9b.sort()
+
+        assert res_5 == [], 'res5: expected %r, got %r' % ([], res_5)
+        assert res_6 == [], 'res6: expected %r, got %r' % ([], res_6)
+        assert res_7 == without_parent, 'res7: expected %r, got %r' % (without_parent, res_7)
+        assert res_8 == [], 'res8: expected %r, got %r' % ([], res_8)
+        assert res_9 == without_parent, 'res9: expected %r, got %r' % (without_parent, res_9)
+        assert res_9b == with_parent, 'res9b: expected %r, got %r' % (with_parent, res_9b)
+        # These queries must return exactly the results than the queries 0..4,
+        # i.e. not ... in ... must be the same as ... not in ... .
+        res_10 = self.search(cr, uid, ['!', ('parent_id', 'like', 'probably_unexisting_name')])
+        res_10.sort()
+        res_11 = self.search(cr, uid, ['!', ('parent_id', 'in', [max_partner_id + 1])])
+        res_11.sort()
+        res_12 = self.search(cr, uid, ['!', ('parent_id', '=', False)])
+        res_12.sort()
+        res_13 = self.search(cr, uid, ['!', ('parent_id', 'in', [])])
+        res_13.sort()
+        res_14 = self.search(cr, uid, ['!', ('parent_id', 'in', [False])])
+        res_14.sort()
+        assert res_0 == res_10
+        assert res_1 == res_11
+        assert res_2 == res_12
+        assert res_3 == res_13
+        assert res_4 == res_14
+
+        # Testing many2one field is not enough, a regular char field is tested
+        # with in [] and must not return any result.
+        res_15 = self.search(cr, uid, [('website', 'in', [])])
+        assert res_15 == []
+        # not in [] must return everything.
+        res_16 = self.search(cr, uid, [('website', 'not in', [])])
+        res_16.sort()
+        assert res_16 == partner_ids
+
+        res_17 = self.search(cr, uid, [('website', '!=', False)])
+        res_17.sort()
+        assert res_17 == with_website
+
+-
+    Check behavior for required many2one fields
+-
+    !python {model: res.company }: |
+        company_ids = sorted(self.search(cr, uid, []))
+        # currency_id is required
+        res_101 = sorted(self.search(cr, uid, [('currency_id', 'not ilike', '')])) # get no companies
+        res_102 = sorted(self.search(cr, uid, [('currency_id', 'ilike', '')])) # get all companies
+        assert res_101 == [], 'res_101: expected %r, got %r' % ([], res_101)
+        assert res_102 == company_ids, 'res_102: expected %r, got %r' % (company_ids, res_102)
+-
+    Verify domain evaluation for `one2many != False`
+-
+    !python {model: res.partner.category }: |
+        all_ids = self.search(cr, uid, [])
+        parent_categs = set([c.parent_id.id for c in self.browse(cr, uid, all_ids) if c.parent_id])
+        result = set(self.search(cr, uid, [('child_ids', '!=', False)]))
+        assert result and result == parent_categs, "Got %r, expected %r" % (result, parent_categs) 
+-
+    Verify domain evaluation for `one2many == False`
+-
+    !python {model: res.partner.category }: |
+        all_ids = self.search(cr, uid, [])
+        parent_categs = set([c.parent_id.id for c in self.browse(cr, uid, all_ids) if c.parent_id])
+        leaf_categs = set(all_ids) - parent_categs
+        result = set(self.search(cr, uid, [('child_ids', '=', False)]))
+        assert result and result == leaf_categs, "Got %r, expected %r" % (result, leaf_categs)
+-
+    Equivalent queries.
+-
+    !python {model: res.currency }: |
+        max_currency_id = max(self.search(cr, uid, []))
+        res_0 = self.search(cr, uid, [])
+        res_1 = self.search(cr, uid, [('name', 'not like', 'probably_unexisting_name')])
+        res_2 = self.search(cr, uid, [('id', 'not in', [max_currency_id + 1003])])
+        res_3 = self.search(cr, uid, [('id', 'not in', [])])
+        res_4 = self.search(cr, uid, [('id', '!=', False)])
+        res_0.sort()
+        res_1.sort()
+        res_2.sort()
+        res_3.sort()
+        res_4.sort()
+        assert res_0 == res_1
+        assert res_0 == res_2
+        assert res_0 == res_3
+        assert res_0 == res_4
+-
+    Equivalent queries, integer and string.
+-
+    !python {model: res.partner }: |
+        all_ids = self.search(cr, uid, [])
+        if len(all_ids) > 1:
+            one = all_ids[0]
+            record = self.browse(cr, uid, one)
+            others = all_ids[1:]
+            res_1 = self.search(cr, uid, [('id', '=', one)])
+            # self.search(cr, uid, [('id', '!=', others)]) # not permitted
+            res_2 = self.search(cr, uid, [('id', 'not in', others)])
+            res_3 = self.search(cr, uid, ['!', ('id', '!=', one)])
+            res_4 = self.search(cr, uid, ['!', ('id', 'in', others)])
+            # res_5 = self.search(cr, uid, [('id', 'in', one)]) # TODO make it permitted, just like for child_of
+            res_6 = self.search(cr, uid, [('id', 'in', [one])])
+            res_7 = self.search(cr, uid, [('name', '=', record.name)])
+            res_8 = self.search(cr, uid, [('name', 'in', [record.name])])
+            # res_9 = self.search(cr, uid, [('name', 'in', record.name)]) # TODO
+            assert [one] == res_1
+            assert [one] == res_2
+            assert [one] == res_3
+            assert [one] == res_4
+            #assert [one] == res_5
+            assert [one] == res_6
+            assert [one] == res_7
+-
+    Need a company with a parent_id.
+-
+    !record {model: res.company, id: ymltest_company3}:
+        name: Acme 3
+-
+    Need a company with a parent_id.
+-
+    !record {model: res.company, id: ymltest_company4}:
+        name: Acme 4
+        parent_id: ymltest_company3
+-
+    Equivalent queries, one2many.
+-
+    !python {model: res.company }: |
+        # Search the company via its one2many (the one2many must point back at the company).
+        company = self.browse(cr, uid, ref('ymltest_company3'))
+        max_currency_id = max(self.pool.get('res.currency').search(cr, uid, []))
+        currency_ids1 = self.pool.get('res.currency').search(cr, uid, [('name', 'not like', 'probably_unexisting_name')])
+        currency_ids2 = self.pool.get('res.currency').search(cr, uid, [('id', 'not in', [max_currency_id + 1003])])
+        currency_ids3 = self.pool.get('res.currency').search(cr, uid, [('id', 'not in', [])])
+        assert currency_ids1 == currency_ids2 == currency_ids3, 'All 3 results should have be the same: all currencies'
+        default_company = self.browse(cr, uid, 1)
+
+        # Due to currency data definition change (in relation with bug 1111298), this test now needs
+        # a manual setup where all currencies are assigned to the default company.
+        self.pool['res.currency'].write(cr, uid, currency_ids1, {'company_id': default_company.id})
+
+        # one2many towards same model
+        res_1 = self.search(cr, uid, [('child_ids', 'in', [x.id for x in company.child_ids])]) # any company having a child of company3 as child
+        res_2 = self.search(cr, uid, [('child_ids', 'in', [company.child_ids[0].id])]) # any company having the first child of company3 as child
+        # one2many towards another model
+        res_3 = self.search(cr, uid, [('currency_ids', 'in', [x.id for x in default_company.currency_ids])]) # companies having a currency of main company
+        res_4 = self.search(cr, uid, [('currency_ids', 'in', [default_company.currency_ids[0].id])]) # companies having first currency of main company
+        res_5 = self.search(cr, uid, [('currency_ids', 'in', default_company.currency_ids[0].id)]) # companies having first currency of main company
+        # res_6 = self.search(cr, uid, [('currency_ids', 'in', [default_company.currency_ids[0].name])]) # TODO
+        res_7 = self.search(cr, uid, [('currency_ids', '=', default_company.currency_ids[0].name)])
+        res_8 = self.search(cr, uid, [('currency_ids', 'like', default_company.currency_ids[0].name)])
+        res_9 = self.search(cr, uid, [('currency_ids', 'like', 'probably_unexisting_name')])
+        # self.search(cr, uid, [('currency_ids', 'unexisting_op', 'probably_unexisting_name')]) # TODO expected exception
+        assert res_1 == [ref('ymltest_company3')]
+        assert res_2 == [ref('ymltest_company3')]
+        assert res_3 == [1]
+        assert res_4 == [1]
+        assert res_5 == [1]
+        assert res_7 == [1]
+        assert res_8 == [1]
+        assert res_9 == []
+
+        # get the companies referenced by some currency (this is normally the main company) using a weird negative domain
+        res_10 = self.search(cr, uid, [('currency_ids', 'not like', 'probably_unexisting_name')])
+        res_11 = self.search(cr, uid, [('currency_ids', 'not in', [max_currency_id + 1])])
+        res_12 = self.search(cr, uid, [('currency_ids', '!=', False)])
+        res_13 = self.search(cr, uid, [('currency_ids', 'not in', [])])
+        res_10.sort()
+        res_11.sort()
+        res_12.sort()
+        res_13.sort()
+        assert res_10 == res_11
+        assert res_10 == res_12
+        assert res_10 == res_13
+        
+        # try testing real subsets with IN/NOT IN
+        res_partner = self.pool.get('res.partner')
+        res_users = self.pool.get('res.users')
+        p1, _ = res_partner.name_create(cr, uid, "Dédé Boitaclou")
+        p2, _ = res_partner.name_create(cr, uid, "Raoulette Pizza O'poil")
+        u1a = res_users.create(cr, uid, {'login': 'dbo', 'partner_id': p1})
+        u1b = res_users.create(cr, uid, {'login': 'dbo2', 'partner_id': p1})
+        u2 = res_users.create(cr, uid, {'login': 'rpo', 'partner_id': p2})
+        assert [p1] == res_partner.search(cr, uid, [('user_ids', 'in', u1a)]), "o2m IN accept single int on right side"
+        assert [p1,p2] == res_partner.search(cr, uid, [('user_ids', 'in', [u1a,u2])]), "o2m IN matches any on the right side"
+        all_partners = res_partner.search(cr, uid, [])
+        assert (set(all_partners) - set([p1])) == set(res_partner.search(cr, uid, [('user_ids', 'not in', u1a)])), "o2m NOT IN matches none on the right side"
+        assert (set(all_partners) - set([p1,p2])) == set(res_partner.search(cr, uid, [('user_ids', 'not in', [u1b, u2])])), "o2m NOT IN matches none on the right side"
+
+        # child_of x returns x and its children (direct or not).
+        company = self.browse(cr, uid, ref('ymltest_company3'))
+        expected = [ref('ymltest_company3'), ref('ymltest_company4')]
+        expected.sort()
+        res_1 = self.search(cr, uid, [('id', 'child_of', [ref('ymltest_company3')])])
+        res_1.sort()
+        res_2 = self.search(cr, uid, [('id', 'child_of', ref('ymltest_company3'))])
+        res_2.sort()
+        res_3 = self.search(cr, uid, [('id', 'child_of', [company.name])])
+        res_3.sort()
+        res_4 = self.search(cr, uid, [('id', 'child_of', company.name)])
+        res_4.sort()
+        assert res_1 == expected
+        assert res_2 == expected
+        assert res_3 == expected
+        assert res_4 == expected
+-
+    Unaccent. Create a company with an accent in its name.
+-
+    !record {model: res.company, id: ymltest_unaccent_company}:
+        name: Hélène
+-
+    Test the unaccent-enabled 'ilike'.
+-
+    !python {model: res.company}: |
+        if self.pool.has_unaccent:
+            ids = self.search(cr, uid, [('name','ilike','Helene')], {})
+            assert ids == [ref('ymltest_unaccent_company')]
+            ids = self.search(cr, uid, [('name','ilike','hélène')], {})
+            assert ids == [ref('ymltest_unaccent_company')]
+            ids = self.search(cr, uid, [('name','not ilike','Helene')], {})
+            assert ref('ymltest_unaccent_company') not in ids
+            ids = self.search(cr, uid, [('name','not ilike','hélène')], {})
+            assert ref('ymltest_unaccent_company') not in ids
+-
+    Check that =like/=ilike expressions (no wildcard variants of like/ilike) are working on an untranslated field.
+-
+    !python {model: res.partner }: |
+        all_ids = self.search(cr, uid, [('name', '=like', 'A_e_or')])
+        assert len(all_ids) == 1, "Must match one partner (Axelor), got %r"%all_ids
+        all_ids = self.search(cr, uid, [('name', '=ilike', 'v%')])
+        assert len(all_ids) >= 1, "Must match one partner (Vicking Direct), got %r"%all_ids
+-
+    Check that =like/=ilike expressions (no wildcard variants of like/ilike) are working on translated field.
+-
+    !python {model: res.country }: |
+        all_ids = self.search(cr, uid, [('name', '=like', 'Ind__')])
+        assert len(all_ids) == 1, "Must match India only, got %r"%all_ids
+        all_ids = self.search(cr, uid, [('name', '=ilike', 'z%')])
+        assert len(all_ids) == 3, "Must match only countries with names starting with Z (currently 3), got %r"%all_ids
+-
+    Use the create_date column on res.country (which doesn't declare it in _columns).
+-
+    !python {model: res.country }: |
+        ids = self.search(cr, uid, [('create_date', '<', '2001-01-01 12:00:00')])
+
+
+-
+    Verify that invalid expressions are refused, even for magic fields
+-
+    !python {model: res.country }: |
+        try:
+            self.search(cr, uid, [('does_not_exist', '=', 'foo')])
+            raise AssertionError('Invalid fields should not be accepted')
+        except ValueError:
+            pass
+
+        try:
+            self.search(cr, uid, [('create_date', '>>', 'foo')])
+            raise AssertionError('Invalid operators should not be accepted')
+        except ValueError:
+            pass
+
+        import psycopg2
+        try:
+            cr._default_log_exceptions = False
+            cr.execute('SAVEPOINT expression_failure_test')
+            self.search(cr, uid, [('create_date', '=', "1970-01-01'); --")])
+            # if the above search gives no error, the operand was not escaped!
+            cr.execute('RELEASE SAVEPOINT expression_failure_test')
+            raise AssertionError('Operands should always be SQL escaped')
+        except psycopg2.DataError:
+            # Should give: 'DataError: invalid input syntax for type timestamp' or similar
+            cr.execute('ROLLBACK TO SAVEPOINT expression_failure_test')
+-
+    Testing for Many2Many field with category supplier and active=False
+-
+    !python {model: res.partner }: |
+        vals = {'category_id': [(6, 0, [ref("base.res_partner_category_1")])],
+                'name': 'OpenERP Test',
+                'active': False,
+                'child_ids': [(0, 0, {'name': 'address of OpenERP Test', 'country_id': ref("base.be")})]
+                }
+        self.create(cr, uid, vals, context=context)
+        res_ids = self.search(cr, uid, [('category_id', 'ilike', 'supplier'), ('active', '=', False)])
+        assert len(res_ids) != 0, "Record not Found with category supplier and active False."
+-
+    Testing for One2Many field with country Belgium and active=False
+-
+    !python {model: res.partner }: |
+        res_ids = self.search(cr, uid, [('child_ids.country_id','=','Belgium'),('active','=',False)])
+        assert len(res_ids) != 0, "Record not Found with country Belgium and active False."
+-
+    Check that we can exclude translated fields (bug lp:1071710)
+-
+    !python {model: res.country}: |
+        # first install french language
+        Modules = self.pool.get('ir.module.module')
+        base = Modules.browse(cr, uid, Modules.search(cr, uid, [('name', '=', 'base')])[0])
+        
+        # hack: mark module as installed to allow install of the translation
+        base_state = base.state
+        base.write({'state': 'installed'})
+        base.update_translations('fr_FR')
+        base.write({'state': base_state})
+
+        # tests
+        be_id = self.search(cr, uid, [('name', '=', 'Belgium')])[0]
+
+        ctx = {'lang': 'fr_FR'}
+        not_be = self.search(cr, uid, [('name', '!=', 'Belgique')], context=ctx)
+
+        assert be_id not in not_be, "Search match failed"
+
+        # indirect search via m2o
+        Partners = self.pool.get('res.partner')
+        agrolait = Partners.search(cr, uid, [('name', '=', 'Agrolait')])[0]
+
+        not_be = Partners.search(cr, uid, [('country_id', '!=', 'Belgium')])
+        assert agrolait not in not_be, "Search match failed (m2o)"
+
+        not_be = Partners.search(cr, uid, [('country_id', '!=', 'Belgique')], context=ctx)
+        assert agrolait not in not_be, "Search match failed (m2o)"