#***************************************************************************
#*   (c) Juergen Riegel (juergen.riegel@web.de) 2003                       *
#*                                                                         *
#*   This file is part of the FreeCAD CAx development system.              *
#*                                                                         *
#*   This program is free software; you can redistribute it and/or modify  *
#*   it under the terms of the GNU Lesser General Public License (LGPL)    *
#*   as published by the Free Software Foundation; either version 2 of     *
#*   the License, or (at your option) any later version.                   *
#*   for detail see the LICENCE text file.                                 *
#*                                                                         *
#*   FreeCAD is distributed in the hope that it will be useful,            *
#*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
#*   GNU Library General Public License for more details.                  *
#*                                                                         *
#*   You should have received a copy of the GNU Library General Public     *
#*   License along with FreeCAD; if not, write to the Free Software        *
#*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
#*   USA                                                                   *
#*                                                                         *
#*   Juergen Riegel 2003                                                   *
#***************************************************************************/

import FreeCAD, os, unittest, tempfile
import math


#---------------------------------------------------------------------------
# define the functions to test the FreeCAD Document code
#---------------------------------------------------------------------------


class DocumentBasicCases(unittest.TestCase):
  def setUp(self):
    self.Doc = FreeCAD.newDocument("CreateTest")

  def testCreateDestroy(self):
    #FIXME: Causes somehow a ref count error but it's _not_ FreeCAD.getDocument()!!!
    #If we remove the whole method no error appears.
    self.failUnless(FreeCAD.getDocument("CreateTest")!= None,"Creating Document failed")

  def testAddition(self):
    # Cannot write a real test case for that but when debugging the
    # C-code there shouldn't be a memory leak (see rev. 1814)
    self.Doc.openTransaction("Add")
    L1 = self.Doc.addObject("App::FeatureTest","Label")
    self.Doc.commitTransaction()
    self.Doc.undo()

  def testAddRemoveUndo(self):
    # Bug #0000525
    self.Doc.openTransaction("Add")
    obj=self.Doc.addObject("App::FeatureTest","Label")
    self.Doc.commitTransaction()
    self.Doc.removeObject(obj.Name)
    self.Doc.undo()
    self.Doc.undo()

  def testAbortTransaction(self):
    self.Doc.openTransaction("Add")
    obj=self.Doc.addObject("App::FeatureTest","Label")
    self.Doc.abortTransaction()
    TempPath = tempfile.gettempdir()
    SaveName = TempPath + os.sep + "SaveRestoreTests.FCStd"
    self.Doc.saveAs(SaveName)

  def testRemoval(self):
    # Cannot write a real test case for that but when debugging the
    # C-code there shouldn't be a memory leak (see rev. 1814)
    self.Doc.openTransaction("Add")
    L1 = self.Doc.addObject("App::FeatureTest","Label")
    self.Doc.commitTransaction()
    self.Doc.openTransaction("Rem")
    L1 = self.Doc.removeObject("Label")
    self.Doc.commitTransaction()

  def testObjects(self):
    L1 = self.Doc.addObject("App::FeatureTest","Label_1")
    #call members to check for errors in ref counting
    self.Doc.ActiveObject
    self.Doc.Objects
    self.Doc.UndoMode
    self.Doc.UndoRedoMemSize
    self.Doc.UndoCount
    # test read only mechanismus
    try:
      self.Doc.UndoCount = 3
    except:
      FreeCAD.Console.PrintLog("   exception thrown, OK\n")
    else:
      self.fail("no exception thrown")
    self.Doc.RedoCount
    self.Doc.UndoNames
    self.Doc.RedoNames
    self.Doc.recompute()
    self.failUnless(L1.Integer == 4711)
    self.failUnless(L1.Float-47.11<0.001)
    self.failUnless(L1.Bool    == True)
    self.failUnless(L1.String  == "4711")
    #temporarily not checked because of strange behavior of boost::fielesystem JR
    #self.failUnless(L1.Path  == "c:/temp")
    self.failUnless(float(L1.Angle)-3.0<0.001)
    self.failUnless(float(L1.Distance)-47.11<0.001)

    # test basic property stuff
    self.failUnless(not L1.getDocumentationOfProperty("Source1") == "")
    self.failUnless(L1.getGroupOfProperty("Source1") == "Feature Test")
    self.failUnless(L1.getTypeOfProperty("Source1") == [])


    # test the constraint types ( both are constraint to percent range)
    self.failUnless(L1.ConstraintInt == 5)
    self.failUnless(L1.ConstraintFloat-5.0<0.001)
    L1.ConstraintInt = 500
    L1.ConstraintFloat = 500.0
    self.failUnless(L1.ConstraintInt == 100)
    self.failUnless(L1.ConstraintFloat - 100.0 < 0.001)
    L1.ConstraintInt = -500
    L1.ConstraintFloat = -500.0
    self.failUnless(L1.ConstraintInt == 0)
    self.failUnless(L1.ConstraintFloat - 0.0 < 0.001)

    # test enum property
    # in App::FeatureTest the current value is set to 4
    self.failUnless(L1.Enum  == "Four")
    L1.Enum = "Three"
    self.failUnless(L1.Enum  == "Three",     "Different value to 'Three'")
    L1.Enum = 2
    self.failUnless(L1.Enum  == "Two",     "Different value to 'Two'")
    try:
      L1.Enum = "SurelyNotInThere!"
    except:
      FreeCAD.Console.PrintLog("   exception thrown, OK\n")
    else:
      self.fail("no exception thrown")

    #self.failUnless(L1.IntegerList  == [4711]   )
    #f = L1.FloatList
    #self.failUnless(f -47.11<0.001    )
    #self.failUnless(L1.Matrix  == [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0] )
    #self.failUnless(L1.Vector  == [1.0,2.0,3.0])

    self.failUnless(L1.Label== "Label_1","Invalid object name")
    L1.Label="Label_2"
    self.Doc.recompute()
    self.failUnless(L1.Label== "Label_2","Invalid object name")
    self.Doc.removeObject("Label_1")

  def testEnum(self):
    enumeration_choices = ["one", "two"]
    obj = self.Doc.addObject("App::FeaturePython","Label_2")
    obj.addProperty("App::PropertyEnumeration", "myEnumeration", "Enum", "mytest")
    with self.assertRaises(ValueError):
      obj.myEnumeration = enumeration_choices[0]

  def testMem(self):
    self.Doc.MemSize

  def testDuplicateLinks(self):
    obj = self.Doc.addObject("App::FeatureTest","obj")
    grp = self.Doc.addObject("App::DocumentObjectGroup","group")
    grp.Group = [obj,obj]
    self.Doc.removeObject(obj.Name)
    self.assertListEqual(grp.Group, [])

  def testAddRemove(self):
    L1 = self.Doc.addObject("App::FeatureTest","Label_1")
    # must delete object
    self.Doc.removeObject(L1.Name)
    try:
      L1.Name
    except:
      self.failUnless(True)
    else:
      self.failUnless(False)
    del L1

    # What do we expect here?
    self.Doc.openTransaction("AddRemove")
    L2 = self.Doc.addObject("App::FeatureTest","Label_2")
    self.Doc.removeObject(L2.Name)
    self.Doc.commitTransaction()
    self.Doc.undo()
    try:
      L2.Name
    except:
      self.failUnless(True)
    else:
      self.failUnless(False)
    del L2
    
  def testExtensions(self):
    #we try to create a normal python object and add a extension to it 
    obj = self.Doc.addObject("App::DocumentObject", "Extension_1")
    grp = self.Doc.addObject("App::DocumentObject", "Extension_2")
    #we should have all methods we need to handle extensions 
    try:
      self.failUnless(not grp.hasExtension("App::GroupExtensionPython"))
      grp.addExtension("App::GroupExtensionPython", self)
      self.failUnless(grp.hasExtension("App::GroupExtension"))
      self.failUnless(grp.hasExtension("App::GroupExtensionPython"))
      grp.addObject(obj)
      self.failUnless(len(grp.Group) == 1)
      self.failUnless(grp.Group[0] == obj)
    except:
      self.failUnless(False)
      
    #test if the method override works
    class SpecialGroup():
        def allowObject(self, obj):
            return False;
    
    callback = SpecialGroup()
    grp2 = self.Doc.addObject("App::DocumentObject", "Extension_3")  
    grp2.addExtension("App::GroupExtensionPython", callback)
    
    try:
      self.failUnless(grp2.hasExtension("App::GroupExtension"))
      grp2.addObject(obj)
      self.failUnless(len(grp2.Group) == 0)
    except:
      self.failUnless(True)
    
    self.Doc.removeObject(grp.Name)
    self.Doc.removeObject(grp2.Name)
    self.Doc.removeObject(obj.Name)
    del obj
    del grp
    del grp2
    
  def testExtensionBug0002785(self):
        
        class MyExtension():
            def __init__(self, obj):
                obj.addExtension("App::GroupExtensionPython", self)
             
        obj = self.Doc.addObject("App::DocumentObject", "myObj")
        MyExtension(obj)
        self.failUnless(obj.hasExtension("App::GroupExtension"))
        self.failUnless(obj.hasExtension("App::GroupExtensionPython"))
        self.Doc.removeObject(obj.Name)
        del obj

  def testExtensionGroup(self):
    obj = self.Doc.addObject("App::DocumentObject", "Obj")
    grp = self.Doc.addObject("App::FeaturePython", "Extension_2")
    grp.addExtension("App::GroupExtensionPython", None)
    grp.Group = [obj]
    self.assertTrue(obj in grp.Group)
    
  def testExtensionBugViewProvider(self):

    class Layer():
      def __init__(self, obj):
        obj.addExtension("App::GroupExtensionPython", self)

    class LayerViewProvider():
      def __init__(self, obj):
        obj.addExtension("Gui::ViewProviderGroupExtensionPython", self)
        obj.Proxy = self
    
    obj = self.Doc.addObject("App::FeaturePython","Layer")
    Layer(obj)
    self.failUnless(obj.hasExtension("App::GroupExtension"))

    if FreeCAD.GuiUp:
        LayerViewProvider(obj.ViewObject)
        self.failUnless(obj.ViewObject.hasExtension("Gui::ViewProviderGroupExtension"))
        self.failUnless(obj.ViewObject.hasExtension("Gui::ViewProviderGroupExtensionPython"))
    
    self.Doc.removeObject(obj.Name)
    del obj
    
  def testPropertyLink_Issue2902Part1(self):
    o1 = self.Doc.addObject("App::FeatureTest","test1")
    o2 = self.Doc.addObject("App::FeatureTest","test2")
    o3 = self.Doc.addObject("App::FeatureTest","test3")

    o1.Link=o2
    self.assertEqual(o1.Link, o2)
    o1.Link=o3
    self.assertEqual(o1.Link, o3)
    o2.Placement = FreeCAD.Placement()
    self.assertEqual(o1.Link, o3)

  def testNotification_Issue2902Part2(self):
    o = self.Doc.addObject("App::FeatureTest","test")

    plm = o.Placement
    o.Placement = FreeCAD.Placement()
    plm.Base.x = 5
    self.assertEqual(o.Placement.Base.x, 0)
    o.Placement.Base.x=5
    self.assertEqual(o.Placement.Base.x, 5)

  def testNotification_Issue2996(self):
    if not FreeCAD.GuiUp:
      return
    # works only if Gui is shown
    class ViewProvider:
      def __init__(self, vobj):
        vobj.Proxy=self

      def attach(self, vobj):
        self.ViewObject = vobj
        self.Object = vobj.Object

      def claimChildren(self):
        children = [self.Object.Link]
        return children

    obj=self.Doc.addObject("App::FeaturePython", "Sketch")
    obj.addProperty("App::PropertyLink","Link")
    ViewProvider(obj.ViewObject)

    ext=self.Doc.addObject("App::FeatureTest", "Extrude")
    ext.Link=obj

    sli=self.Doc.addObject("App::FeaturePython", "Slice")
    sli.addProperty("App::PropertyLink","Link").Link=ext
    ViewProvider(sli.ViewObject)

    com=self.Doc.addObject("App::FeaturePython", "CompoundFilter")
    com.addProperty("App::PropertyLink", "Link").Link=sli
    ViewProvider(com.ViewObject)

    ext.Label="test"

    self.assertEqual(ext.Link, obj)
    self.assertNotEqual(ext.Link, sli)

  def tearDown(self):
    #closing doc
    FreeCAD.closeDocument("CreateTest")

# class must be defined in global scope to allow it to be reloaded on document open
class SaveRestoreSpecialGroup():
    def __init__(self, obj):
        obj.addExtension("App::GroupExtensionPython", self)
        obj.Proxy = self
        
    def allowObject(self, obj):
        return False;

# class must be defined in global scope to allow it to be reloaded on document open    
class SaveRestoreSpecialGroupViewProvider():
    def __init__(self, obj):
        obj.addExtension("Gui::ViewProviderGroupExtensionPython", self)
        obj.Proxy = self
        
    def testFunction(self):
        pass
        
class DocumentSaveRestoreCases(unittest.TestCase):
  def setUp(self):
    self.Doc = FreeCAD.newDocument("SaveRestoreTests")
    L1 = self.Doc.addObject("App::FeatureTest","Label_1")
    L2 = self.Doc.addObject("App::FeatureTest","Label_2")
    L3 = self.Doc.addObject("App::FeatureTest","Label_3")
    self.TempPath = tempfile.gettempdir()
    FreeCAD.Console.PrintLog( '  Using temp path: ' + self.TempPath + '\n')

  def testSaveAndRestore(self):
    # saving and restoring
    SaveName = self.TempPath + os.sep + "SaveRestoreTests.FCStd"
    self.failUnless(self.Doc.Label_1.TypeTransient == 4711)
    self.Doc.Label_1.TypeTransient = 4712
    # setup Linking
    self.Doc.Label_1.Link = self.Doc.Label_2
    self.Doc.Label_2.Link = self.Doc.Label_3
    self.Doc.Label_1.LinkSub = (self.Doc.Label_2,["Sub1","Sub2"])
    self.Doc.Label_2.LinkSub = (self.Doc.Label_3,["Sub3","Sub4"])
    # save the document
    self.Doc.saveAs(SaveName)
    FreeCAD.closeDocument("SaveRestoreTests")
    self.Doc = FreeCAD.open(SaveName)
    self.failUnless(self.Doc.Label_1.Integer == 4711)
    self.failUnless(self.Doc.Label_2.Integer == 4711)
    # test Linkage
    self.failUnless(self.Doc.Label_1.Link == self.Doc.Label_2)
    self.failUnless(self.Doc.Label_2.Link == self.Doc.Label_3)
    self.failUnless(self.Doc.Label_1.LinkSub == (self.Doc.Label_2,["Sub1","Sub2"]))
    self.failUnless(self.Doc.Label_2.LinkSub == (self.Doc.Label_3,["Sub3","Sub4"]))
    # do  NOT save transient properties
    self.failUnless(self.Doc.Label_1.TypeTransient == 4711)
    self.failUnless(self.Doc == FreeCAD.getDocument(self.Doc.Name))

  def testRestore(self):
    Doc = FreeCAD.newDocument("RestoreTests")
    Doc.addObject("App::FeatureTest","Label_1")
    # saving and restoring
    FileName = self.TempPath + os.sep + "Test2.FCStd"
    Doc.saveAs(FileName)
    # restore must first clear the current content
    Doc.restore()
    self.failUnless(len(Doc.Objects) == 1)
    FreeCAD.closeDocument("RestoreTests")

  def testActiveDocument(self):
    # open 2nd doc
    Second = FreeCAD.newDocument("Active")
    FreeCAD.closeDocument("Active")
    try:
        # There might be no active document anymore
        # This also checks for dangling pointers
        Active = FreeCAD.activeDocument()
        # Second is still a valid object
        self.failUnless(Second != Active)
    except:
        # Okay, no document open
        self.failUnless(True)
        
  def testExtensionSaveRestore(self):
    # saving and restoring
    SaveName = self.TempPath + os.sep + "SaveRestoreExtensions.FCStd"
    Doc = FreeCAD.newDocument("SaveRestoreExtensions")
    #we try to create a normal python object and add a extension to it 
    obj  = Doc.addObject("App::DocumentObject", "Obj") 
    grp1 = Doc.addObject("App::DocumentObject", "Extension_1")
    grp2 = Doc.addObject("App::FeaturePython", "Extension_2") 
    
    grp1.addExtension("App::GroupExtensionPython", None)
    SaveRestoreSpecialGroup(grp2)
    if FreeCAD.GuiUp:
        SaveRestoreSpecialGroupViewProvider(grp2.ViewObject)
    grp2.Group = [obj]
    
    Doc.saveAs(SaveName)
    FreeCAD.closeDocument("SaveRestoreExtensions")
    Doc = FreeCAD.open(SaveName)
    
    self.failUnless(Doc.Extension_1.hasExtension("App::GroupExtension"))
    self.failUnless(Doc.Extension_2.hasExtension("App::GroupExtension"))
    self.failUnless(Doc.Extension_1.ExtensionProxy is None)
    self.failUnless(Doc.Extension_2.ExtensionProxy is not None)
    self.failUnless(Doc.Extension_2.Group[0] is Doc.Obj)
    self.failUnless(hasattr(Doc.Extension_2.Proxy, 'allowObject'))
    self.failUnless(hasattr(Doc.Extension_2.ExtensionProxy, 'allowObject'))

    if FreeCAD.GuiUp:
      self.failUnless(Doc.Extension_2.ViewObject.hasExtension("Gui::ViewProviderGroupExtensionPython"))
      self.failUnless(hasattr(Doc.Extension_2.ViewObject.Proxy, 'testFunction'))
      self.failUnless(hasattr(Doc.Extension_2.ViewObject.ExtensionProxy, 'testFunction'))

    FreeCAD.closeDocument("SaveRestoreExtensions")

  def tearDown(self):
    #closing doc
    FreeCAD.closeDocument("SaveRestoreTests")

class DocumentRecomputeCases(unittest.TestCase):
  def setUp(self):
    self.Doc = FreeCAD.newDocument("RecomputeTests")
    self.L1 = self.Doc.addObject("App::FeatureTest","Label_1")
    self.L2 = self.Doc.addObject("App::FeatureTest","Label_2")
    self.L3 = self.Doc.addObject("App::FeatureTest","Label_3")

  def testDescent(self):
    # testing the up and downstream stuff
    FreeCAD.Console.PrintLog("def testDescent(self):Testcase not implemented\n")
    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
    FreeCAD.closeDocument("RecomputeTests")

class UndoRedoCases(unittest.TestCase):
  def setUp(self):
    self.Doc = FreeCAD.newDocument("UndoTest")
    self.Doc.UndoMode = 0
    self.Doc.addObject("App::FeatureTest","Base")
    self.Doc.addObject("App::FeatureTest","Del")
    self.Doc.getObject("Del").Integer  = 2

  def testUndoProperties(self):
    # switch on the Undo
    self.Doc.UndoMode = 1

    # first transaction
    self.Doc.openTransaction("Transaction1")
    self.Doc.addObject("App::FeatureTest","test1")
    self.Doc.getObject("test1").Integer  = 1
    self.Doc.getObject("test1").String   = "test1"
    self.Doc.getObject("test1").Float    = 1.0
    self.Doc.getObject("test1").Bool  = 1

    #self.Doc.getObject("test1").IntegerList  = 1
    #self.Doc.getObject("test1").FloatList  = 1.0

    #self.Doc.getObject("test1").Matrix  = (1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0)
    #self.Doc.getObject("test1").Vector  = (1.0,1.0,1.0)

    # second transaction
    self.Doc.openTransaction("Transaction2")
    self.Doc.getObject("test1").Integer  = 2
    self.Doc.getObject("test1").String   = "test2"
    self.Doc.getObject("test1").Float    = 2.0
    self.Doc.getObject("test1").Bool  = 0

    # switch on the Undo OFF
    self.Doc.UndoMode = 0

  def testUndoClear(self):
    # switch on the Undo
    self.Doc.UndoMode = 1
    self.assertEqual(self.Doc.UndoNames,[])
    self.assertEqual(self.Doc.UndoCount,0)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)

    self.Doc.openTransaction("Transaction1")
    # becomes the active object
    self.Doc.addObject("App::FeatureTest","test1")
    self.Doc.commitTransaction()
    # removes the active object
    self.Doc.undo()
    self.assertEqual(self.Doc.ActiveObject,None)
    # deletes the active object
    self.Doc.clearUndos()
    self.assertEqual(self.Doc.ActiveObject,None)

  def testUndo(self):
    # switch on the Undo
    self.Doc.UndoMode = 1
    self.assertEqual(self.Doc.UndoNames,[])
    self.assertEqual(self.Doc.UndoCount,0)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)

    # first transaction
    self.Doc.openTransaction("Transaction1")
    self.Doc.addObject("App::FeatureTest","test1")
    self.Doc.getObject("test1").Integer  = 1
    self.Doc.getObject("Del").Integer  = 1
    self.Doc.removeObject("Del")
    self.assertEqual(self.Doc.UndoNames,['Transaction1'])
    self.assertEqual(self.Doc.UndoCount,1)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)

    # second transaction
    self.Doc.openTransaction("Transaction2")
    self.assertEqual(self.Doc.UndoNames,['Transaction2','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,2)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)
    self.Doc.getObject("test1").Integer  = 2
    self.assertEqual(self.Doc.UndoNames,['Transaction2','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,2)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)

    # abort second transaction
    self.Doc.abortTransaction()
    self.assertEqual(self.Doc.UndoNames,['Transaction1'])
    self.assertEqual(self.Doc.UndoCount,1)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)
    self.assertEqual(self.Doc.getObject("test1").Integer, 1)

    # again second transaction
    self.Doc.openTransaction("Transaction2")
    self.Doc.getObject("test1").Integer  = 2
    self.assertEqual(self.Doc.UndoNames,['Transaction2','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,2)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)

    # third transaction
    self.Doc.openTransaction("Transaction3")
    self.assertEqual(self.Doc.UndoNames,['Transaction3','Transaction2','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,3)
    self.Doc.getObject("test1").Integer  = 3
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)

    # fourth transaction
    self.Doc.openTransaction("Transaction4")
    self.assertEqual(self.Doc.UndoNames,['Transaction4','Transaction3','Transaction2','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,4)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)
    self.Doc.getObject("test1").Integer  = 4

    # undo the fourth transaction
    self.Doc.undo()
    self.assertEqual(self.Doc.getObject("test1").Integer, 3)
    self.assertEqual(self.Doc.UndoNames,['Transaction3','Transaction2','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,3)
    self.assertEqual(self.Doc.RedoNames,['Transaction4'])
    self.assertEqual(self.Doc.RedoCount,1)

    # undo the third transaction
    self.Doc.undo()
    self.assertEqual(self.Doc.getObject("test1").Integer, 2)
    self.assertEqual(self.Doc.UndoNames,['Transaction2','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,2)
    self.assertEqual(self.Doc.RedoNames,['Transaction3','Transaction4'])
    self.assertEqual(self.Doc.RedoCount,2)

    # undo the second transaction
    self.Doc.undo()
    self.assertEqual(self.Doc.getObject("test1").Integer, 1)
    self.assertEqual(self.Doc.UndoNames,['Transaction1'])
    self.assertEqual(self.Doc.UndoCount,1)
    self.assertEqual(self.Doc.RedoNames,['Transaction2','Transaction3','Transaction4'])
    self.assertEqual(self.Doc.RedoCount,3)

    # undo the first transaction
    self.Doc.undo()
    self.failUnless(self.Doc.getObject("test1") == None)
    self.failUnless(self.Doc.getObject("Del").Integer == 2)
    self.assertEqual(self.Doc.UndoNames,[])
    self.assertEqual(self.Doc.UndoCount,0)
    self.assertEqual(self.Doc.RedoNames,['Transaction1','Transaction2','Transaction3','Transaction4'])
    self.assertEqual(self.Doc.RedoCount,4)

    # redo the first transaction
    self.Doc.redo()
    self.assertEqual(self.Doc.getObject("test1").Integer, 1)
    self.assertEqual(self.Doc.UndoNames,['Transaction1'])
    self.assertEqual(self.Doc.UndoCount,1)
    self.assertEqual(self.Doc.RedoNames,['Transaction2','Transaction3','Transaction4'])
    self.assertEqual(self.Doc.RedoCount,3)

    # redo the second transaction
    self.Doc.redo()
    self.assertEqual(self.Doc.getObject("test1").Integer, 2)
    self.assertEqual(self.Doc.UndoNames,['Transaction2','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,2)
    self.assertEqual(self.Doc.RedoNames,['Transaction3','Transaction4'])
    self.assertEqual(self.Doc.RedoCount,2)

    # undo the second transaction
    self.Doc.undo()
    self.assertEqual(self.Doc.getObject("test1").Integer, 1)
    self.assertEqual(self.Doc.UndoNames,['Transaction1'])
    self.assertEqual(self.Doc.UndoCount,1)
    self.assertEqual(self.Doc.RedoNames,['Transaction2','Transaction3','Transaction4'])
    self.assertEqual(self.Doc.RedoCount,3)

    # new transaction eight
    self.Doc.openTransaction("Transaction8")
    self.Doc.getObject("test1").Integer  = 8
    self.assertEqual(self.Doc.UndoNames,['Transaction8','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,2)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)
    self.Doc.abortTransaction()
    self.assertEqual(self.Doc.UndoNames,['Transaction1'])
    self.assertEqual(self.Doc.UndoCount,1)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)

    # again new transaction eight
    self.Doc.openTransaction("Transaction8")
    self.Doc.getObject("test1").Integer  = 8
    self.assertEqual(self.Doc.UndoNames,['Transaction8','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,2)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)

    # again new transaction nine
    self.Doc.openTransaction("Transaction9")
    self.Doc.getObject("test1").Integer  = 9
    self.assertEqual(self.Doc.UndoNames,['Transaction9','Transaction8','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,3)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)
    self.Doc.commitTransaction()
    self.assertEqual(self.Doc.UndoNames,['Transaction9','Transaction8','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,3)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)
    self.assertEqual(self.Doc.getObject("test1").Integer, 9)

    # undo the ninth transaction
    self.Doc.undo()
    self.assertEqual(self.Doc.getObject("test1").Integer, 8)
    self.assertEqual(self.Doc.UndoNames,['Transaction8','Transaction1'])
    self.assertEqual(self.Doc.UndoCount,2)
    self.assertEqual(self.Doc.RedoNames,['Transaction9'])
    self.assertEqual(self.Doc.RedoCount,1)

    # switch on the Undo OFF
    self.Doc.UndoMode = 0
    self.assertEqual(self.Doc.UndoNames,[])
    self.assertEqual(self.Doc.UndoCount,0)
    self.assertEqual(self.Doc.RedoNames,[])
    self.assertEqual(self.Doc.RedoCount,0)

  def testUndoInList(self):
    
    self.Doc.UndoMode = 1
      
    self.Doc.openTransaction("Box")
    self.Box = self.Doc.addObject('Part::Box')
    self.Doc.commitTransaction()

    self.Doc.openTransaction("Cylinder")
    self.Cylinder = self.Doc.addObject('Part::Cylinder')
    self.Doc.commitTransaction()
    
    self.Doc.openTransaction("Fuse")
    self.Fuse1 = self.Doc.addObject('Part::MultiFuse', 'Fuse')
    self.Fuse1.Shapes = [self.Box, self.Cylinder]
    self.Doc.commitTransaction()
    
    self.Doc.undo()
    self.failUnless(len(self.Box.InList) == 0)
    self.failUnless(len(self.Cylinder.InList) == 0)      
    
    self.Doc.redo()
    self.failUnless(len(self.Box.InList) == 1)
    self.failUnless(self.Box.InList[0] == self.Doc.Fuse)
    self.failUnless(len(self.Cylinder.InList) == 1)  
    self.failUnless(self.Cylinder.InList[0] == self.Doc.Fuse)
      
  def testUndoIssue0003150Part1(self):
  
    self.Doc.UndoMode = 1
  
    self.Doc.openTransaction("Box")
    self.Box = self.Doc.addObject('Part::Box')
    self.Doc.commitTransaction()

    self.Doc.openTransaction("Cylinder")
    self.Cylinder = self.Doc.addObject('Part::Cylinder')
    self.Doc.commitTransaction()

    self.Doc.openTransaction("Fuse")
    self.Fuse1 = self.Doc.addObject('Part::MultiFuse')
    self.Fuse1.Shapes = [self.Box, self.Cylinder]
    self.Doc.commitTransaction()
    self.Doc.recompute()

    self.Doc.openTransaction("Sphere")
    self.Sphere = self.Doc.addObject('Part::Sphere')
    self.Doc.commitTransaction()

    self.Doc.openTransaction("Fuse")
    self.Fuse2 = self.Doc.addObject('Part::MultiFuse')
    self.Fuse2.Shapes = [self.Fuse1, self.Sphere]
    self.Doc.commitTransaction()
    self.Doc.recompute()

    self.Doc.openTransaction("Part")
    self.Part = self.Doc.addObject('App::Part')
    self.Doc.commitTransaction()

    self.Doc.openTransaction("Drag")
    self.Part.addObject(self.Fuse2)
    self.Doc.commitTransaction()

    #3 undos show the problem of failing recompute
    self.Doc.undo()
    self.Doc.undo()
    self.Doc.undo()
    self.failUnless(self.Doc.recompute() >= 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")
    G1 = self.Doc.addObject("App::DocumentObjectGroup","Group")
    G1.addObject(L2)
    self.failUnless(G1.hasObject(L2))

    # Adding the group to itself must fail
    try:
      G1.addObject(G1)
    except:
      FreeCAD.Console.PrintLog("Cannot add group to itself, OK\n")
    else:
      self.fail("Adding the group to itself must not be possible")

    self.Doc.UndoMode = 1

    # Remove object from group
    self.Doc.openTransaction("Remove")
    self.Doc.removeObject("Label_2")
    self.Doc.commitTransaction()
    self.failUnless(G1.getObject("Label_2") == None)
    self.Doc.undo()
    self.failUnless(G1.getObject("Label_2") != None)

    # Remove first group and then the object
    self.Doc.openTransaction("Remove")
    self.Doc.removeObject("Group")
    self.Doc.removeObject("Label_2")
    self.Doc.commitTransaction()
    self.Doc.undo()
    self.failUnless(G1.getObject("Label_2") != None)

    # Remove first object and then the group in two transactions
    self.Doc.openTransaction("Remove")
    self.Doc.removeObject("Label_2")
    self.Doc.commitTransaction()
    self.failUnless(G1.getObject("Label_2") == None)
    self.Doc.openTransaction("Remove")
    self.Doc.removeObject("Group")
    self.Doc.commitTransaction()
    self.Doc.undo()
    self.Doc.undo()
    self.failUnless(G1.getObject("Label_2") != None)

    # Remove first object and then the group in one transaction
    self.Doc.openTransaction("Remove")
    self.Doc.removeObject("Label_2")
    self.failUnless(G1.getObject("Label_2") == None)
    self.Doc.removeObject("Group")
    self.Doc.commitTransaction()
    self.Doc.undo()
    # FIXME: See bug #1820554
    self.failUnless(G1.getObject("Label_2") != None)

    # Add a second object to the group
    L3 = self.Doc.addObject("App::FeatureTest","Label_3")
    G1.addObject(L3)
    self.Doc.openTransaction("Remove")
    self.Doc.removeObject("Label_2")
    self.failUnless(G1.getObject("Label_2") == None)
    self.Doc.removeObject("Label_3")
    self.failUnless(G1.getObject("Label_3") == None)
    self.Doc.removeObject("Group")
    self.Doc.commitTransaction()
    self.Doc.undo()
    self.failUnless(G1.getObject("Label_3") != None)
    self.failUnless(G1.getObject("Label_2") != None)

    self.Doc.UndoMode = 0

    # Cleanup
    self.Doc.removeObject("Group")
    self.Doc.removeObject("Label_2")
    self.Doc.removeObject("Label_3")
    
  def testGroupAndGeoFeatureGroup(self):
    
    # an object can only be in one group at once, that must be enforced
    obj1 = self.Doc.addObject("App::FeatureTest","obj1")
    grp1 = self.Doc.addObject("App::DocumentObjectGroup","Group1")
    grp2 = self.Doc.addObject("App::DocumentObjectGroup","Group2")
    grp1.addObject(obj1)
    self.failUnless(obj1.getParentGroup()==grp1)
    self.failUnless(obj1.getParentGeoFeatureGroup()==None)
    self.failUnless(grp1.hasObject(obj1))
    grp2.addObject(obj1)
    self.failUnless(grp1.hasObject(obj1)==False)
    self.failUnless(grp2.hasObject(obj1))
    
    # an object is allowed to be in a group and a geofeaturegroup
    prt1 = self.Doc.addObject("App::Part","Part1")
    prt2 = self.Doc.addObject("App::Part","Part2")
    
    prt1.addObject(grp2)
    self.failUnless(grp2.getParentGeoFeatureGroup()==prt1)
    self.failUnless(grp2.getParentGroup()==None)
    self.failUnless(grp2.hasObject(obj1))
    self.failUnless(prt1.hasObject(grp2))
    self.failUnless(prt1.hasObject(obj1))
    
    #it is not allowed to be in 2 geofeaturegroups 
    prt2.addObject(grp2)
    self.failUnless(grp2.hasObject(obj1))
    self.failUnless(prt1.hasObject(grp2)==False)
    self.failUnless(prt1.hasObject(obj1)==False)
    self.failUnless(prt2.hasObject(grp2))
    self.failUnless(prt2.hasObject(obj1))
    try:
        grp = prt1.Group
        grp.append(obj1)
        prt1.Group = grp
    except:
        grp.remove(obj1)
        self.failUnless(prt1.Group == grp)
    else:
        self.fail("No exception thrown when object is in multiple Groups")
          
    #it is not allowed to be in 2 Groups
    prt2.addObject(grp1)
    grp = grp1.Group
    grp.append(obj1)
    try:
        grp1.Group = grp
    except:
        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")
    cyl = self.Doc.addObject("Part::Cylinder","Cylinder")
    fus = self.Doc.addObject("Part::MultiFuse","Fusion")
    fus.Shapes = [cyl, box]
    self.Doc.recompute()
    self.failUnless(fus.State[0] == 'Up-to-date')
    fus.Shapes = [] #remove all links as addObject would otherwise transfer all linked objects
    prt1.addObject(cyl)
    fus.Shapes = [cyl, box]
    self.Doc.recompute()
    #self.failUnless(fus.State[0] == 'Invalid')
    fus.Shapes = []
    prt1.addObject(box)
    fus.Shapes = [cyl, box]
    self.Doc.recompute()
    #self.failUnless(fus.State[0] == 'Invalid')
    fus.Shapes = []
    prt1.addObject(fus)
    fus.Shapes = [cyl, box]
    self.Doc.recompute()
    self.failUnless(fus.State[0] == 'Up-to-date')
    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
    self.Doc.recompute()
    prt2.Group = []
    try:
        prt2.Group = [prt2]
    except:
        pass
    else:
        self.fail("Exception is expected")
        
    self.Doc.recompute()
    
  def testIssue0003150Part2(self):
    self.box = self.Doc.addObject("Part::Box")
    self.cyl = self.Doc.addObject("Part::Cylinder")
    self.sph = self.Doc.addObject("Part::Sphere")

    self.fus1 = self.Doc.addObject("Part::MultiFuse")
    self.fus2 = self.Doc.addObject("Part::MultiFuse")

    self.fus1.Shapes = [self.box, self.cyl];
    self.fus2.Shapes = [self.sph, self.cyl];

    self.prt = self.Doc.addObject("App::Part")
    self.prt.addObject(self.fus1)
    self.failUnless(len(self.prt.Group)==5)
    self.failUnless(self.fus2.getParentGeoFeatureGroup() == self.prt)
    self.failUnless(self.prt.hasObject(self.sph))
    
    self.prt.removeObject(self.fus1)
    self.failUnless(len(self.prt.Group)==0)

  def tearDown(self):
    # closing doc
    FreeCAD.closeDocument("GroupTests")

class DocumentPlatformCases(unittest.TestCase):
  def setUp(self):
    self.Doc = FreeCAD.newDocument("PlatformTests")
    self.Doc.addObject("App::FeatureTest", "Test")
    self.TempPath = tempfile.gettempdir()
    self.DocName = self.TempPath + os.sep + "PlatformTests.FCStd"

  def testFloatList(self):
    self.Doc.Test.FloatList = [-0.05, 2.5, 5.2]

    # saving and restoring
    self.Doc.saveAs(self.DocName)
    FreeCAD.closeDocument("PlatformTests")
    self.Doc = FreeCAD.open(self.DocName)

    self.failUnless(abs(self.Doc.Test.FloatList[0] + .05) < 0.01)
    self.failUnless(abs(self.Doc.Test.FloatList[1] - 2.5) < 0.01)
    self.failUnless(abs(self.Doc.Test.FloatList[2] - 5.2) < 0.01)

  def testColorList(self):
    self.Doc.Test.ColourList = [(1.0,0.5,0.0),(0.0,0.5,1.0)]

    # saving and restoring
    self.Doc.saveAs(self.DocName)
    FreeCAD.closeDocument("PlatformTests")
    self.Doc = FreeCAD.open(self.DocName)

    self.failUnless(abs(self.Doc.Test.ColourList[0][0] - 1.0) < 0.01)
    self.failUnless(abs(self.Doc.Test.ColourList[0][1] - 0.5) < 0.01)
    self.failUnless(abs(self.Doc.Test.ColourList[0][2] - 0.0) < 0.01)
    self.failUnless(abs(self.Doc.Test.ColourList[0][3] - 0.0) < 0.01)
    self.failUnless(abs(self.Doc.Test.ColourList[1][0] - 0.0) < 0.01)
    self.failUnless(abs(self.Doc.Test.ColourList[1][1] - 0.5) < 0.01)
    self.failUnless(abs(self.Doc.Test.ColourList[1][2] - 1.0) < 0.01)
    self.failUnless(abs(self.Doc.Test.ColourList[1][3] - 0.0) < 0.01)

  def testVectorList(self):
    self.Doc.Test.VectorList = [(-0.05, 2.5, 5.2),(-0.05, 2.5, 5.2)]

    # saving and restoring
    self.Doc.saveAs(self.DocName)
    FreeCAD.closeDocument("PlatformTests")
    self.Doc = FreeCAD.open(self.DocName)

    self.failUnless(len(self.Doc.Test.VectorList) == 2)

  def testPoints(self):
    try:
      self.Doc.addObject("Points::Feature", "Points")

      # saving and restoring
      self.Doc.saveAs(self.DocName)
      FreeCAD.closeDocument("PlatformTests")
      self.Doc = FreeCAD.open(self.DocName)

      self.failUnless(self.Doc.Points.Points.count() == 0)
    except:
      pass

  def tearDown(self):
    #closing doc
    FreeCAD.closeDocument("PlatformTests")


class DocumentBacklinks(unittest.TestCase):
  def setUp(self):
    self.Doc = FreeCAD.newDocument("BackLinks")

  def testIssue0003323(self):
    self.Doc.UndoMode=1
    self.Doc.openTransaction("Create object")
    obj1=self.Doc.addObject("App::FeatureTest","Test1")
    obj2=self.Doc.addObject("App::FeatureTest","Test2")
    obj2.Link=obj1
    self.Doc.commitTransaction()
    self.Doc.undo()
    self.Doc.openTransaction("Create object")

  def tearDown(self):
    # closing doc
    FreeCAD.closeDocument("BackLinks")


class DocumentFileIncludeCases(unittest.TestCase):
  def setUp(self):
    self.Doc = FreeCAD.newDocument("FileIncludeTests")
    # testing with undo
    self.Doc.UndoMode = 1


  def testApplyFiles(self):
    self.Doc.openTransaction("Transaction0")
    self.L1 = self.Doc.addObject("App::DocumentObjectFileIncluded","FileObject1")
    self.failUnless(self.L1.File =="")
    self.Filename = self.L1.File

    self.Doc.openTransaction("Transaction1")
    self.TempPath = tempfile.gettempdir()
    # creating a file in the Transient directory of the document
    file = open(self.Doc.getTempFileName("test"),"w")
    file.write("test No1")
    file.close()
    # applying the file
    self.L1.File = (file.name,"Test.txt")
    self.failUnless(self.L1.File.split("/")[-1] == "Test.txt")
    # read again
    file = open(self.L1.File,"r")
    self.failUnless(file.read()=="test No1")
    file.close()
    file = open(self.TempPath+"/testNest.txt","w")
    file.write("test No2")
    file.close()
    # applying the file
    self.Doc.openTransaction("Transaction2")
    self.L1.File = file.name
    self.failUnless(self.L1.File.split("/")[-1] == "Test.txt")
    # read again
    file = open(self.L1.File,"r")
    self.failUnless(file.read()=="test No2")
    file.close()
    self.Doc.undo()
    self.failUnless(self.L1.File.split("/")[-1] == "Test.txt")
    # read again
    file = open(self.L1.File,"r")
    self.failUnless(file.read()=="test No1")
    file.close()
    self.Doc.undo()
    # read again
    self.failUnless(self.L1.File == "")
    self.Doc.redo()
    self.failUnless(self.L1.File.split("/")[-1] == "Test.txt")
    # read again
    file = open(self.L1.File,"r")
    self.failUnless(file.read()=="test No1")
    file.close()
    self.Doc.redo()
    self.failUnless(self.L1.File.split("/")[-1] == "Test.txt")
    # read again
    file = open(self.L1.File,"r")
    self.failUnless(file.read()=="test No2")
    file.close()
    # Save restore test
    FileName = self.TempPath+"/FileIncludeTests.fcstd"
    self.Doc.saveAs(FileName)
    FreeCAD.closeDocument("FileIncludeTests")
    self.Doc = FreeCAD.open(self.TempPath+"/FileIncludeTests.fcstd")
    # check if the file is still there
    self.L1 = self.Doc.getObject("FileObject1")
    file = open(self.L1.File,"r")
    res = file.read()
    FreeCAD.Console.PrintLog( res +"\n")
    self.failUnless(res=="test No2")
    self.failUnless(self.L1.File.split("/")[-1] == "Test.txt")
    file.close()

    # test for bug #94 (File overlap in PropertyFileIncluded)
    L2 = self.Doc.addObject("App::DocumentObjectFileIncluded","FileObject2")
    L3 = self.Doc.addObject("App::DocumentObjectFileIncluded","FileObject3")

    # creating two files in the Transient directory of the document
    file1 = open(self.Doc.getTempFileName("test"),"w")
    file1.write("test No1")
    file1.close()
    file2 = open(self.Doc.getTempFileName("test"),"w")
    file2.write("test No2")
    file2.close()

    # applying the file with the same base name
    L2.File = (file1.name,"Test.txt")
    L3.File = (file2.name,"Test.txt")

    file = open(L2.File,"r")
    self.failUnless(file.read()=="test No1")
    file.close()
    file = open(L3.File,"r")
    self.failUnless(file.read()=="test No2")
    file.close()

    # create a second document, copy a file and close the document
    # the test is about to put the file to the correct transient dir
    doc2 = FreeCAD.newDocument("Doc2")
    L4 = doc2.addObject("App::DocumentObjectFileIncluded","FileObject")
    L5 = doc2.addObject("App::DocumentObjectFileIncluded","FileObject")
    L6 = doc2.addObject("App::DocumentObjectFileIncluded","FileObject")
    L4.File = (L3.File,"Test.txt")
    L5.File = L3.File
    L6.File = L3.File
    FreeCAD.closeDocument("FileIncludeTests")
    self.Doc = FreeCAD.open(self.TempPath+"/FileIncludeTests.fcstd")
    self.failUnless(os.path.exists(L4.File))
    self.failUnless(os.path.exists(L5.File))
    self.failUnless(os.path.exists(L6.File))
    self.failUnless(L5.File != L6.File)
    # copy file from L5 which is in the same directory
    L7 = doc2.addObject("App::DocumentObjectFileIncluded","FileObject3")
    L7.File = (L5.File,"Copy.txt")
    self.failUnless(os.path.exists(L5.File))
    FreeCAD.closeDocument("Doc2")


  def tearDown(self):
    #closing doc
    FreeCAD.closeDocument("FileIncludeTests")


class DocumentPropertyCases(unittest.TestCase):
  def setUp(self):
    self.Doc = FreeCAD.newDocument("PropertyTests")
    self.Obj = self.Doc.addObject("App::FeaturePython","Test")

  def testDescent(self):
    # testing the up and downstream stuff
    props=self.Obj.supportedProperties()
    for i in props:
        self.Obj.addProperty(i,i)
    tempPath = tempfile.gettempdir()
    tempFile = tempPath + os.sep + "PropertyTests.FCStd"
    self.Doc.saveAs(tempFile)
    FreeCAD.closeDocument("PropertyTests")
    self.Doc = FreeCAD.open(tempFile)

  def testRemoveProperty(self):
    prop = 'Something'
    self.Obj.addProperty('App::PropertyFloat', prop)
    self.Obj.Something = 0.01
    self.Doc.recompute()
    self.Doc.openTransaction('modify and remove property')
    self.Obj.Something = 0.00
    self.Obj.removeProperty(prop)
    self.Obj.recompute()
    self.Doc.abortTransaction()

  def tearDown(self):
    #closing doc
    FreeCAD.closeDocument("PropertyTests")


class DocumentExpressionCases(unittest.TestCase):
  def setUp(self):
    self.Doc = FreeCAD.newDocument()
    self.Obj1 = self.Doc.addObject("App::FeatureTest","Test")
    self.Obj2 = self.Doc.addObject("App::FeatureTest","Test")

  def assertAlmostEqual (self, v1, v2) :
    if (math.fabs(v2-v1) > 1E-12) :
      self.assertEqual(v1,v2)

			
  def testExpression(self):
    # set the object twice to test that the backlinks are removed when overwriting the expression
    self.Obj2.setExpression('Placement.Rotation.Angle', u'%s.Placement.Rotation.Angle' % self.Obj1.Name)
    self.Obj2.setExpression('Placement.Rotation.Angle', u'%s.Placement.Rotation.Angle' % self.Obj1.Name)
    self.Obj1.Placement = FreeCAD.Placement(FreeCAD.Vector(0,0,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),10))
    self.Doc.recompute()
    self.assertAlmostEqual(self.Obj1.Placement.Rotation.Angle, self.Obj2.Placement.Rotation.Angle)
	
    # clear the expression
    self.Obj2.setExpression('Placement.Rotation.Angle', None)
    self.assertAlmostEqual(self.Obj1.Placement.Rotation.Angle, self.Obj2.Placement.Rotation.Angle)
    self.Doc.recompute()
    self.assertAlmostEqual(self.Obj1.Placement.Rotation.Angle, self.Obj2.Placement.Rotation.Angle)
    # touch the objects to perform a recompute
    self.Obj1.Placement = self.Obj1.Placement
    self.Obj2.Placement = self.Obj2.Placement
    # must not raise a topological error
    self.assertEqual(self.Doc.recompute(), 2)

  def tearDown(self):
    #closing doc
    FreeCAD.closeDocument(self.Doc.Name)
