Prevent crash on cyclic group relations. fixes #0002567

This commit is contained in:
Stefan Tröger
2017-07-29 10:41:05 +02:00
committed by wmayer
parent 553514bd15
commit 03721b91b8
2 changed files with 93 additions and 78 deletions

View File

@@ -1161,7 +1161,7 @@ void DocumentItem::populateItem(DocumentObjectItem *item, bool refresh) {
if(!createNewItem(*childItem->object(),item,i,it->second))
--i;
}else {
if(item->isChildOfItem(childItem)) {
if(item==childItem || item->isChildOfItem(childItem)) {
Base::Console().Error("Gui::DocumentItem::populateItem(): Cyclic dependency in %s and %s\n",
item->object()->getObject()->Label.getValue(),
childItem->object()->getObject()->Label.getValue());

View File

@@ -271,80 +271,6 @@ class DocumentBasicCases(unittest.TestCase):
self.Doc.removeObject(obj.Name)
del obj
def testRecompute(self):
# sequence to test recompute behaviour
# L1---\ L7
# / \ \ |
# L2 L3 \ L8
# / \ / \ /
# L4 L5 L6
L1 = self.Doc.addObject("App::FeatureTest","Label_1")
L2 = self.Doc.addObject("App::FeatureTest","Label_2")
L3 = self.Doc.addObject("App::FeatureTest","Label_3")
L4 = self.Doc.addObject("App::FeatureTest","Label_4")
L5 = self.Doc.addObject("App::FeatureTest","Label_5")
L6 = self.Doc.addObject("App::FeatureTest","Label_6")
L7 = self.Doc.addObject("App::FeatureTest","Label_7")
L8 = self.Doc.addObject("App::FeatureTest","Label_8")
L1.LinkList = [L2,L3,L6]
L2.Link = L4
L2.LinkList = [L5]
L3.LinkList = [L5,L6]
L7.Link = L8 #make second root
self.failUnless(L7 in self.Doc.RootObjects)
self.failUnless(L1 in self.Doc.RootObjects)
self.failUnless(len(self.Doc.Objects) == len(self.Doc.TopologicalSortedObjects))
seqDic = {}
i = 0
for obj in self.Doc.TopologicalSortedObjects:
seqDic[obj] = i
print(obj)
i += 1
self.failUnless(seqDic[L2] > seqDic[L1])
self.failUnless(seqDic[L3] > seqDic[L1])
self.failUnless(seqDic[L5] > seqDic[L2])
self.failUnless(seqDic[L5] > seqDic[L3])
self.failUnless(seqDic[L5] > seqDic[L1])
self.failUnless((0, 0, 0, 0, 0, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
self.failUnless(self.Doc.recompute()==4)
self.failUnless((1, 1, 1, 0, 0, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L5.touch()
self.failUnless((1, 1, 1, 0, 0, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
self.failUnless(self.Doc.recompute()==4)
self.failUnless((2, 2, 2, 0, 1, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L4.touch()
self.failUnless(self.Doc.recompute()==3)
self.failUnless((3, 3, 2, 1, 1, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L5.touch()
self.failUnless(self.Doc.recompute()==4)
self.failUnless((4, 4, 3, 1, 2, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L6.touch()
self.failUnless(self.Doc.recompute()==3)
self.failUnless((5, 4, 4, 1, 2, 1)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L2.touch()
self.failUnless(self.Doc.recompute()==2)
self.failUnless((6, 5, 4, 1, 2, 1)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L1.touch()
self.failUnless(self.Doc.recompute()==1)
self.failUnless((7, 5, 4, 1, 2, 1)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
self.Doc.removeObject(L1.Name)
self.Doc.removeObject(L2.Name)
self.Doc.removeObject(L3.Name)
self.Doc.removeObject(L4.Name)
self.Doc.removeObject(L5.Name)
self.Doc.removeObject(L6.Name)
self.Doc.removeObject(L7.Name)
self.Doc.removeObject(L8.Name)
def testPropertyLink_Issue2902Part1(self):
o1 = self.Doc.addObject("App::FeatureTest","test1")
o2 = self.Doc.addObject("App::FeatureTest","test2")
@@ -535,6 +461,79 @@ class DocumentRecomputeCases(unittest.TestCase):
self.L1.Link = self.L2
self.L2.Link = self.L3
def testRecompute(self):
# sequence to test recompute behaviour
# L1---\ L7
# / \ \ |
# L2 L3 \ L8
# / \ / \ /
# L4 L5 L6
L1 = self.Doc.addObject("App::FeatureTest","Label_1")
L2 = self.Doc.addObject("App::FeatureTest","Label_2")
L3 = self.Doc.addObject("App::FeatureTest","Label_3")
L4 = self.Doc.addObject("App::FeatureTest","Label_4")
L5 = self.Doc.addObject("App::FeatureTest","Label_5")
L6 = self.Doc.addObject("App::FeatureTest","Label_6")
L7 = self.Doc.addObject("App::FeatureTest","Label_7")
L8 = self.Doc.addObject("App::FeatureTest","Label_8")
L1.LinkList = [L2,L3,L6]
L2.Link = L4
L2.LinkList = [L5]
L3.LinkList = [L5,L6]
L7.Link = L8 #make second root
self.failUnless(L7 in self.Doc.RootObjects)
self.failUnless(L1 in self.Doc.RootObjects)
self.failUnless(len(self.Doc.Objects) == len(self.Doc.TopologicalSortedObjects))
seqDic = {}
i = 0
for obj in self.Doc.TopologicalSortedObjects:
seqDic[obj] = i
print(obj)
i += 1
self.failUnless(seqDic[L2] > seqDic[L1])
self.failUnless(seqDic[L3] > seqDic[L1])
self.failUnless(seqDic[L5] > seqDic[L2])
self.failUnless(seqDic[L5] > seqDic[L3])
self.failUnless(seqDic[L5] > seqDic[L1])
self.failUnless((0, 0, 0, 0, 0, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
self.failUnless(self.Doc.recompute()==4)
self.failUnless((1, 1, 1, 0, 0, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L5.touch()
self.failUnless((1, 1, 1, 0, 0, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
self.failUnless(self.Doc.recompute()==4)
self.failUnless((2, 2, 2, 0, 1, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L4.touch()
self.failUnless(self.Doc.recompute()==3)
self.failUnless((3, 3, 2, 1, 1, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L5.touch()
self.failUnless(self.Doc.recompute()==4)
self.failUnless((4, 4, 3, 1, 2, 0)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L6.touch()
self.failUnless(self.Doc.recompute()==3)
self.failUnless((5, 4, 4, 1, 2, 1)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L2.touch()
self.failUnless(self.Doc.recompute()==2)
self.failUnless((6, 5, 4, 1, 2, 1)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
L1.touch()
self.failUnless(self.Doc.recompute()==1)
self.failUnless((7, 5, 4, 1, 2, 1)==(L1.ExecCount,L2.ExecCount,L3.ExecCount,L4.ExecCount,L5.ExecCount,L6.ExecCount))
self.Doc.removeObject(L1.Name)
self.Doc.removeObject(L2.Name)
self.Doc.removeObject(L3.Name)
self.Doc.removeObject(L4.Name)
self.Doc.removeObject(L5.Name)
self.Doc.removeObject(L6.Name)
self.Doc.removeObject(L7.Name)
self.Doc.removeObject(L8.Name)
def tearDown(self):
#closing doc
@@ -765,6 +764,15 @@ class UndoRedoCases(unittest.TestCase):
self.assertEqual(self.Doc.RedoNames,[])
self.assertEqual(self.Doc.RedoCount,0)
def tearDown(self):
# closing doc
FreeCAD.closeDocument("UndoTest")
class DocumentGroupCases(unittest.TestCase):
def setUp(self):
self.Doc = FreeCAD.newDocument("GroupTests")
def testGroup(self):
# Add an object to the group
L2 = self.Doc.addObject("App::FeatureTest","Label_2")
@@ -893,7 +901,7 @@ class UndoRedoCases(unittest.TestCase):
pass
else:
self.fail("No exception thrown when object is in multiple Groups")
#cross linking between GeoFeatureGroups is not allowed
self.Doc.recompute()
box = self.Doc.addObject("Part::Box","Box")
@@ -920,11 +928,18 @@ class UndoRedoCases(unittest.TestCase):
prt2.addObject(box) #this time addObject should move all dependencies to the new part
self.Doc.recompute()
self.failUnless(fus.State[0] == 'Up-to-date')
#grouping must be resilient against cyclic links and not crash: #issue 0002567
prt1.addObject(prt2)
grp = prt2.Group
grp.append(prt1)
prt2.Group = grp
prt1.Group = [prt1]
prt2.Group = [prt2]
def tearDown(self):
# closing doc
FreeCAD.closeDocument("UndoTest")
FreeCAD.closeDocument("GroupTests")
class DocumentPlatformCases(unittest.TestCase):
def setUp(self):