- | 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 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}: | from tools.safe_eval import literal_eval expected = (1, {"a": 9}, (True, False, None)) actual = 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}: | from tools.safe_eval import literal_eval try: 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}: | from tools.safe_eval import literal_eval try: 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 tools.safe_eval import safe_eval try: safe_eval('open("/etc/passwd","r")') assert False, "safe_eval should not allow calling open() builtin" except NameError: pass except: # NameError should be raised because open() builtin is not found, # but other exceptions probably indicate that open() was executed! assert False, "safe_eval should not allow calling open() builtin" - "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 - "7.1 Float precision tests: verify that float rounding methods are working correctly via res.currency" - !python {model: res.currency}: | currency = self.browse(cr, uid, ref('base.EUR')) def try_round(amount, expected, self=self, cr=cr, currency=currency): result = str(self.round(cr, 1, currency, amount)) 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.0') try_round(-0.001,'-0.0') try_round(0.0049,'0.0') # 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 # test from Bug 882036 def try_round(amount, expected, self=self, cr=cr, currency=currency): result = str(self.round(cr, 1, currency, amount)) assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected) try_round(-1.674, '-1.67') try_round(-2.674, '-2.67') try_round(-3.674, '-3.67') try_round(-0.675, '-0.68') try_round(-1.675, '-1.68') try_round(-2.675, '-2.68') try_round(-3.675, '-3.68') try_round(-0.676, '-0.68') try_round(-1.676, '-1.68') try_round(-2.676, '-2.68') try_round(-3.676, '-3.68') try_round(0.674, '0.67') try_round(1.674, '1.67') try_round(2.674, '2.67') try_round(3.674, '3.67') try_round(0.675, '0.68') try_round(1.675, '1.68') try_round(2.675, '2.68') try_round(3.675, '3.68') try_round(0.676, '0.68') try_round(1.676, '1.68') try_round(2.676, '2.68') try_round(3.676, '3.68') 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) - "7.2 Float precision tests: verify that float rounding methods are working correctly via tools" - !python {model: res.currency}: | from tools import float_compare, float_is_zero, float_round def try_round(amount, expected, precision_digits=3, float_round=float_round): result = str(float_round(amount, precision_digits=precision_digits)) assert result == expected, 'Rounding error 7.2.1: 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.0') try_round(-0.0004, '-0.0') #try_round(357.4555, '357.456') #try_round(-357.4555, '-357.456') try_round(457.4554, '457.455') try_round(-457.4554, '-457.455') try_round(-0.674, '-0.674') # test from Bug 882036 def try_round(amount, expected, precision_digits=2, float_round=float_round): result = str(float_round(amount, precision_digits=precision_digits)) assert result == expected, 'Rounding error 7.2.2: got %s, expected %s' % (result, expected) try_round(-1.674, '-1.67') try_round(-2.674, '-2.67') try_round(-3.674, '-3.67') try_round(-0.675, '-0.68') try_round(-1.675, '-1.68') try_round(-2.675, '-2.68') try_round(-3.675, '-3.68') try_round(-0.676, '-0.68') try_round(-1.676, '-1.68') try_round(-2.676, '-2.68') try_round(-3.676, '-3.68') try_round(0.674, '0.67') try_round(1.674, '1.67') try_round(2.674, '2.67') try_round(3.674, '3.67') try_round(0.675, '0.68') try_round(1.675, '1.68') try_round(2.675, '2.68') try_round(3.675, '3.68') try_round(0.676, '0.68') try_round(1.676, '1.68') try_round(2.676, '2.68') try_round(3.676, '3.68') 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 different precision_roundings def try_round(amount, expected, precision_rounding=None, float_round=float_round): result = str(float_round(amount, precision_rounding=precision_rounding)) assert result == expected, 'Rounding error 7.2.3: got %s, expected %s' % (result, expected) try_round(-457.4554, '-457.45', precision_rounding=0.05) try_round(457.444, '457.5', precision_rounding=0.5) try_round(457.3, '455.0', precision_rounding=5) try_round(457.5, '460.0', precision_rounding=5) try_round(457.1, '456.0', precision_rounding=3) - "7.3 Float precision tests: verify that invalid parameters are forbidden" - !python {model: res.currency}: | from 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 - "8. base_test.yml finished" -