This happens when multiple parent are modified at the same time. In such case the new path previously computed is no more correct. issue11266 review368141002
414 lines
11 KiB
Python
414 lines
11 KiB
Python
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
|
|
import unittest
|
|
|
|
from trytond.model.exceptions import DomainValidationError, RecursionError
|
|
from trytond.pool import Pool
|
|
from trytond.tests.test_tryton import activate_module, with_transaction
|
|
|
|
|
|
class TreeTestCaseMixin:
|
|
model_name = None
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
activate_module('tests')
|
|
|
|
def create(self):
|
|
pool = Pool()
|
|
Model = pool.get(self.model_name)
|
|
|
|
new_records = [None, None, None]
|
|
for j in range(3):
|
|
parent_records = new_records
|
|
new_records = []
|
|
k = 0
|
|
to_create = []
|
|
for parent_record in parent_records:
|
|
to_create.extend([{
|
|
'name': 'Test %d %d %d' % (j, k, i),
|
|
'parent': (parent_record.id
|
|
if parent_record else None),
|
|
} for i in range(3)])
|
|
k += 1
|
|
new_records = Model.create(to_create)
|
|
|
|
def check_tree(self):
|
|
raise NotImplementedError
|
|
|
|
def change_parent(self, parent=None, restore=False):
|
|
pool = Pool()
|
|
Model = pool.get(self.model_name)
|
|
|
|
records = Model.search([
|
|
('parent', '=', parent),
|
|
])
|
|
if not records:
|
|
return
|
|
for record in records[::2]:
|
|
for record2 in records[1::2]:
|
|
record.parent = record2
|
|
record.save()
|
|
if restore:
|
|
record.parent = parent
|
|
record.save()
|
|
for record in records:
|
|
self.change_parent(record, restore=restore)
|
|
|
|
@with_transaction()
|
|
def test_create(self):
|
|
"Test create tree"
|
|
self.create()
|
|
self.check_tree()
|
|
|
|
@with_transaction()
|
|
def test_reparent(self):
|
|
"Test re-parent"
|
|
self.create()
|
|
self.change_parent(restore=True)
|
|
self.check_tree()
|
|
|
|
@with_transaction()
|
|
def test_move(self):
|
|
"Test move"
|
|
self.create()
|
|
self.change_parent()
|
|
self.check_tree()
|
|
|
|
@with_transaction()
|
|
def test_active(self):
|
|
"Test active"
|
|
pool = Pool()
|
|
Model = pool.get(self.model_name)
|
|
|
|
self.create()
|
|
|
|
records = Model.search([])
|
|
for record in records:
|
|
if record.id % 2:
|
|
record.active = False
|
|
record.save()
|
|
self.check_tree()
|
|
self.change_parent()
|
|
self.check_tree()
|
|
|
|
records = Model.search([])
|
|
Model.write(records, {
|
|
'active': True,
|
|
})
|
|
Model.write(records[::2], {
|
|
'active': False
|
|
})
|
|
self.check_tree()
|
|
|
|
records = Model.search([])
|
|
Model.write(records, {
|
|
'active': True,
|
|
})
|
|
Model.write(records[:len(records) // 2], {
|
|
'active': False
|
|
})
|
|
self.check_tree()
|
|
|
|
records = Model.search([])
|
|
Model.write(records, {
|
|
'active': False
|
|
})
|
|
self.change_parent()
|
|
self.check_tree()
|
|
|
|
@with_transaction()
|
|
def test_delete(self):
|
|
"Test delete"
|
|
pool = Pool()
|
|
Model = pool.get(self.model_name)
|
|
|
|
self.create()
|
|
|
|
records = Model.search([])
|
|
for record in records:
|
|
if record.id % 2:
|
|
Model.delete([record])
|
|
self.check_tree()
|
|
|
|
records = Model.search([])
|
|
Model.delete(records[:len(records) // 2])
|
|
self.check_tree()
|
|
|
|
records = Model.search([])
|
|
Model.delete(records)
|
|
self.check_tree()
|
|
|
|
@with_transaction()
|
|
def test_write_multiple_parents(self):
|
|
"Test write multiple parents"
|
|
pool = Pool()
|
|
Model = pool.get(self.model_name)
|
|
|
|
record1 = Model(name="Root")
|
|
record1.save()
|
|
record2 = Model(name="Child", parent=record1)
|
|
record2.save()
|
|
record3 = Model(name="Grand Child", parent=record2)
|
|
record3.save()
|
|
self.check_tree()
|
|
|
|
Model.write([record2, record3], {'parent': None})
|
|
self.check_tree()
|
|
|
|
def rebuild(self):
|
|
raise NotImplementedError
|
|
|
|
@with_transaction()
|
|
def test_rebuild(self):
|
|
"Test rebuild"
|
|
self.create()
|
|
|
|
self.rebuild()
|
|
|
|
self.check_tree()
|
|
|
|
|
|
class TreeMixinTestCase(unittest.TestCase):
|
|
"Test TreeMixin"
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
activate_module('tests')
|
|
|
|
@with_transaction()
|
|
def test_name_domain(self):
|
|
"Test name domain"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
record = Tree(name="foo / bar")
|
|
|
|
with self.assertRaises(DomainValidationError):
|
|
record.save()
|
|
|
|
@with_transaction()
|
|
def test_rec_name(self):
|
|
"Test rec_name"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
|
|
self.assertEqual(record.rec_name, "parent / record")
|
|
|
|
@with_transaction()
|
|
def test_search_rec_name_equals(self):
|
|
"Test search_rec_name equals"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
|
|
records = Tree.search([('rec_name', '=', 'parent / record')])
|
|
|
|
self.assertEqual(records, [record])
|
|
|
|
@with_transaction()
|
|
def test_search_rec_name_equals_toplevel(self):
|
|
"Test search_rec_name equals top-level"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
|
|
records = Tree.search([('rec_name', '=', 'parent')])
|
|
|
|
self.assertEqual(records, [parent])
|
|
|
|
@with_transaction()
|
|
def test_search_rec_name_equals_none(self):
|
|
"Test search_rec_name equals"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
|
|
records = Tree.search([('rec_name', '=', None)])
|
|
|
|
self.assertEqual(records, [])
|
|
|
|
@with_transaction()
|
|
def test_search_rec_name_non_equals(self):
|
|
"Test search_rec_name non equals"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
|
|
records = Tree.search([('rec_name', '!=', 'parent / record')])
|
|
|
|
self.assertEqual(records, [parent])
|
|
|
|
@with_transaction()
|
|
def test_search_rec_name_non_equals_toplevel(self):
|
|
"Test search_rec_name non equals top-level"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
|
|
records = Tree.search([('rec_name', '!=', 'parent')])
|
|
|
|
self.assertEqual(records, [record])
|
|
|
|
@with_transaction()
|
|
def test_search_rec_name_non_equals_none(self):
|
|
"Test search_rec_name equals"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
|
|
records = Tree.search([('rec_name', '!=', None)])
|
|
|
|
self.assertEqual(records, [parent, record])
|
|
|
|
@with_transaction()
|
|
def test_search_rec_name_in(self):
|
|
"Test search_rec_name in"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
|
|
records = Tree.search([('rec_name', 'in', ['parent / record'])])
|
|
|
|
self.assertEqual(records, [record])
|
|
|
|
@with_transaction()
|
|
def test_search_rec_name_in_toplevel(self):
|
|
"Test search_rec_name in top-level"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
|
|
records = Tree.search([('rec_name', 'in', ['parent'])])
|
|
|
|
self.assertEqual(records, [parent])
|
|
|
|
@with_transaction()
|
|
def test_search_rec_name_like(self):
|
|
"Test search_rec_name like"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
child = Tree(name="child", parent=record)
|
|
child.save()
|
|
|
|
records = Tree.search([('rec_name', 'like', '%record%')])
|
|
|
|
self.assertEqual(records, [record, child])
|
|
|
|
@with_transaction()
|
|
def test_search_rec_name_like_toplevel(self):
|
|
"Test search_rec_name like top-level"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
child = Tree(name="child", parent=record)
|
|
child.save()
|
|
|
|
records = Tree.search([('rec_name', 'like', 'parent / record%')])
|
|
|
|
self.assertEqual(records, [record, child])
|
|
|
|
@with_transaction()
|
|
def test_search_rec_name_like_lowlevel(self):
|
|
"Test search_rec_name like low-level"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
child = Tree(name="child", parent=record)
|
|
child.save()
|
|
|
|
records = Tree.search([('rec_name', 'like', '%record')])
|
|
|
|
self.assertEqual(records, [record])
|
|
|
|
@with_transaction()
|
|
def test_check_recursion(self):
|
|
"Test check_recursion"
|
|
pool = Pool()
|
|
Tree = pool.get('test.tree')
|
|
|
|
parent = Tree(name="parent")
|
|
parent.save()
|
|
record = Tree(name="record", parent=parent)
|
|
record.save()
|
|
child = Tree(name="child", parent=record)
|
|
child.save()
|
|
|
|
with self.assertRaises(RecursionError):
|
|
parent.parent = child
|
|
parent.save()
|
|
|
|
@with_transaction()
|
|
def test_check_recursion_polytree(self):
|
|
"Test check_recursion on polytree"
|
|
pool = Pool()
|
|
Polytree = pool.get('test.polytree')
|
|
|
|
parent1 = Polytree(name="parent1")
|
|
parent1.save()
|
|
parent2 = Polytree(name="parent2")
|
|
parent2.save()
|
|
record = Polytree(name="record", parents=[parent1, parent2])
|
|
record.save()
|
|
child = Polytree(name="child", parents=[record])
|
|
child.save()
|
|
|
|
with self.assertRaises(RecursionError):
|
|
parent1.parents = [child]
|
|
parent1.save()
|
|
|
|
|
|
def suite():
|
|
return unittest.TestLoader().loadTestsFromTestCase(TreeMixinTestCase)
|