From 237060bc955399194e6104ba612aa6fa633a0f08 Mon Sep 17 00:00:00 2001
From: Furgo <148809153+furgo16@users.noreply.github.com>
Date: Mon, 14 Jul 2025 18:47:18 +0200
Subject: [PATCH] BIM: set up TestArchGui, clean up CLI/GUI tests
- Also update TechDraw templates path after reorganization from https://github.com/FreeCAD/FreeCAD/pull/23719
---
src/Mod/BIM/CMakeLists.txt | 5 +-
src/Mod/BIM/TestArch.py | 2 +-
src/Mod/BIM/TestArchGui.py | 206 +-----------------
src/Mod/BIM/bimtests/TestArch.py | 150 -------------
src/Mod/BIM/bimtests/TestArchBase.py | 31 ++-
src/Mod/BIM/bimtests/TestArchBaseGui.py | 55 +++++
.../BIM/bimtests/TestArchBuildingPartGui.py | 79 +++++++
src/Mod/BIM/bimtests/TestArchComponent.py | 27 +++
src/Mod/BIM/bimtests/TestArchEquipment.py | 13 +-
src/Mod/BIM/bimtests/TestArchFrame.py | 12 +-
src/Mod/BIM/bimtests/TestArchImportersGui.py | 88 ++++++++
src/Mod/BIM/bimtests/TestArchRebar.py | 25 ++-
src/Mod/BIM/bimtests/TestArchSectionPlane.py | 48 +++-
src/Mod/BIM/bimtests/TestArchStructure.py | 34 +++
src/Mod/BIM/bimtests/TestArchWindow.py | 41 ++--
15 files changed, 432 insertions(+), 384 deletions(-)
delete mode 100644 src/Mod/BIM/bimtests/TestArch.py
create mode 100644 src/Mod/BIM/bimtests/TestArchBaseGui.py
create mode 100644 src/Mod/BIM/bimtests/TestArchBuildingPartGui.py
create mode 100644 src/Mod/BIM/bimtests/TestArchImportersGui.py
create mode 100644 src/Mod/BIM/bimtests/TestArchStructure.py
diff --git a/src/Mod/BIM/CMakeLists.txt b/src/Mod/BIM/CMakeLists.txt
index 338ab06294..e018875b81 100644
--- a/src/Mod/BIM/CMakeLists.txt
+++ b/src/Mod/BIM/CMakeLists.txt
@@ -202,9 +202,9 @@ SET(nativeifc_SRCS
)
SET(bimtests_SRCS
- bimtests/TestArch.py
bimtests/TestArchAxis.py
bimtests/TestArchBase.py
+ bimtests/TestArchBaseGui.py
bimtests/TestArchComponent.py
bimtests/TestArchBuildingPart.py
bimtests/TestArchRoof.py
@@ -214,6 +214,7 @@ SET(bimtests_SRCS
bimtests/TestArchPanel.py
bimtests/TestArchWindow.py
bimtests/TestArchStairs.py
+ bimtests/TestArchStructure.py
bimtests/TestArchPipe.py
bimtests/TestArchCurtainWall.py
bimtests/TestArchProfile.py
@@ -229,6 +230,8 @@ SET(bimtests_SRCS
bimtests/TestArchTruss.py
bimtests/TestWebGLExport.py
bimtests/TestWebGLExportGui.py
+ bimtests/TestArchImportersGui.py
+ bimtests/TestArchBuildingPartGui.py
)
SOURCE_GROUP("" FILES ${Arch_SRCS})
diff --git a/src/Mod/BIM/TestArch.py b/src/Mod/BIM/TestArch.py
index 03288827c6..9e2f4cc038 100644
--- a/src/Mod/BIM/TestArch.py
+++ b/src/Mod/BIM/TestArch.py
@@ -28,7 +28,7 @@ from bimtests.TestArchSpace import TestArchSpace
from bimtests.TestArchWall import TestArchWall
from bimtests.TestArchBuildingPart import TestArchBuildingPart
from bimtests.TestArchAxis import TestArchAxis
-from bimtests.TestArch import TestArch
+from bimtests.TestArchStructure import TestArchStructure
from bimtests.TestArchMaterial import TestArchMaterial
from bimtests.TestArchPanel import TestArchPanel
from bimtests.TestArchWindow import TestArchWindow
diff --git a/src/Mod/BIM/TestArchGui.py b/src/Mod/BIM/TestArchGui.py
index d08f0b7fe9..ce41d2188d 100644
--- a/src/Mod/BIM/TestArchGui.py
+++ b/src/Mod/BIM/TestArchGui.py
@@ -22,208 +22,8 @@
# * *
# ***************************************************************************
-# Unit test for the Arch module
-
-import os
-import unittest
-
-import FreeCAD as App
-from FreeCAD import Units
-
-import Arch
-import Draft
-import Part
-import Sketcher
-import TechDraw
-import WorkingPlane
-
-from draftutils.messages import _msg
-
-if App.GuiUp:
- import FreeCADGui
+"""Import all Arch module unit tests in GUI mode."""
+from bimtests.TestArchImportersGui import TestArchImportersGui
+from bimtests.TestArchBuildingPartGui import TestArchBuildingPartGui
from bimtests.TestWebGLExportGui import TestWebGLExportGui
-
-class ArchTest(unittest.TestCase):
-
- def setUp(self):
- # setting a new document to hold the tests
- if App.ActiveDocument:
- if App.ActiveDocument.Name != "ArchTest":
- App.newDocument("ArchTest")
- else:
- App.newDocument("ArchTest")
- App.setActiveDocument("ArchTest")
-
- def testRebar(self):
- App.Console.PrintLog ('Checking Arch Rebar…\n')
- s = Arch.makeStructure(length=2,width=3,height=5)
- sk = App.ActiveDocument.addObject('Sketcher::SketchObject','Sketch')
- sk.AttachmentSupport = (s,["Face6"])
- sk.addGeometry(Part.LineSegment(App.Vector(-0.85,1.25,0),App.Vector(0.75,1.25,0)))
- sk.addGeometry(Part.LineSegment(App.Vector(0.75,1.25,0),App.Vector(0.75,-1.20,0)))
- sk.addGeometry(Part.LineSegment(App.Vector(0.75,-1.20,0),App.Vector(-0.85,-1.20,0)))
- sk.addGeometry(Part.LineSegment(App.Vector(-0.85,-1.20,0),App.Vector(-0.85,1.25,0)))
- sk.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1))
- sk.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1))
- sk.addConstraint(Sketcher.Constraint('Coincident',2,2,3,1))
- sk.addConstraint(Sketcher.Constraint('Coincident',3,2,0,1))
- r = Arch.makeRebar(s,sk,diameter=.1,amount=2)
- self.assertTrue(r,"Arch Rebar failed")
-
- def testBuildingPart(self):
- """Create a BuildingPart from a wall with a window and check its shape.
- """
- # Also regression test for:
- # https://github.com/FreeCAD/FreeCAD/issues/6178
- operation = "Arch BuildingPart"
- _msg(" Test '{}'".format(operation))
- # Most of the code below taken from testWindow function.
- line = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(3000, 0, 0))
- wall = Arch.makeWall(line)
- sk = App.ActiveDocument.addObject("Sketcher::SketchObject", "Sketch001")
- sk.Placement.Rotation = App.Rotation(App.Vector(1, 0, 0), 90)
- sk.addGeometry(Part.LineSegment(App.Vector( 500, 800, 0), App.Vector(1500, 800, 0)))
- sk.addGeometry(Part.LineSegment(App.Vector(1500, 800, 0), App.Vector(1500, 2000, 0)))
- sk.addGeometry(Part.LineSegment(App.Vector(1500, 2000, 0), App.Vector( 500, 2000, 0)))
- sk.addGeometry(Part.LineSegment(App.Vector( 500, 2000, 0), App.Vector( 500, 800, 0)))
- sk.addConstraint(Sketcher.Constraint('Coincident', 0, 2, 1, 1))
- sk.addConstraint(Sketcher.Constraint('Coincident', 1, 2, 2, 1))
- sk.addConstraint(Sketcher.Constraint('Coincident', 2, 2, 3, 1))
- sk.addConstraint(Sketcher.Constraint('Coincident', 3, 2, 0, 1))
- App.ActiveDocument.recompute()
- win = Arch.makeWindow(sk)
- Arch.removeComponents(win, host=wall)
- App.ActiveDocument.recompute()
- bp = Arch.makeBuildingPart()
-
- # Wall visibility works when standalone
- FreeCADGui.Selection.clearSelection()
- FreeCADGui.Selection.addSelection('ArchTest',wall.Name)
- assert wall.Visibility
- FreeCADGui.runCommand('Std_ToggleVisibility',0)
- App.ActiveDocument.recompute()
- assert not wall.Visibility
- FreeCADGui.runCommand('Std_ToggleVisibility',0)
- assert wall.Visibility
-
- bp.Group = [wall]
- App.ActiveDocument.recompute()
- # Fails with OCC 7.5
- # self.assertTrue(len(bp.Shape.Faces) == 16, "'{}' failed".format(operation))
-
- # Wall visibility works when inside a BuildingPart
- FreeCADGui.runCommand('Std_ToggleVisibility',0)
- App.ActiveDocument.recompute()
- assert not wall.Visibility
- FreeCADGui.runCommand('Std_ToggleVisibility',0)
- assert wall.Visibility
-
- # Wall visibility works when BuildingPart Toggled
- FreeCADGui.Selection.clearSelection()
- FreeCADGui.Selection.addSelection('ArchTest',bp.Name)
- FreeCADGui.runCommand('Std_ToggleVisibility',0)
- assert not wall.Visibility
- FreeCADGui.runCommand('Std_ToggleVisibility',0)
- assert wall.Visibility
-
- # Wall visibiity works inside group inside BuildingPart Toggled
- grp = App.ActiveDocument.addObject("App::DocumentObjectGroup","Group")
- grp.Label="Group"
- grp.Group = [wall]
- bp.Group = [grp]
- App.ActiveDocument.recompute()
- assert wall.Visibility
- FreeCADGui.runCommand('Std_ToggleVisibility',0)
- App.ActiveDocument.recompute()
- assert not wall.Visibility
- FreeCADGui.runCommand('Std_ToggleVisibility',0)
- App.ActiveDocument.recompute()
- assert wall.Visibility
-
- def testImportSH3D(self):
- """Import a SweetHome 3D file
- """
- operation = "importers.importSH3D"
- _msg(" Test '{}'".format(operation))
- import BIM.importers.importSH3DHelper
- importer = BIM.importers.importSH3DHelper.SH3DImporter(None)
- importer.import_sh3d_from_string(SH3D_HOME)
- assert App.ActiveDocument.Site
- assert App.ActiveDocument.BuildingPart.Label == "Building"
- assert App.ActiveDocument.BuildingPart001.Label == "Level"
- assert App.ActiveDocument.Wall
-
- def tearDown(self):
- App.closeDocument("ArchTest")
- pass
-
-
-SH3D_HOME = """
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-"""
diff --git a/src/Mod/BIM/bimtests/TestArch.py b/src/Mod/BIM/bimtests/TestArch.py
deleted file mode 100644
index f1933d0c7f..0000000000
--- a/src/Mod/BIM/bimtests/TestArch.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-# ***************************************************************************
-# * *
-# * Copyright (c) 2013 Yorik van Havre *
-# * Copyright (c) 2025 Furgo *
-# * *
-# * This file is part of FreeCAD. *
-# * *
-# * FreeCAD is free software: you can redistribute it and/or modify it *
-# * under the terms of the GNU Lesser General Public License as *
-# * published by the Free Software Foundation, either version 2.1 of the *
-# * License, or (at your option) any later version. *
-# * *
-# * 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 *
-# * Lesser General Public License for more details. *
-# * *
-# * You should have received a copy of the GNU Lesser General Public *
-# * License along with FreeCAD. If not, see *
-# * . *
-# * *
-# ***************************************************************************
-
-import os
-import FreeCAD as App
-import Arch
-import Draft
-import Part
-import Sketcher
-import Arch
-from bimtests import TestArchBase
-from draftutils.messages import _msg
-
-# TODO: move these tests to their relevant modules, remove this file
-class TestArch(TestArchBase.TestArchBase):
-
- def testStructure(self):
- App.Console.PrintLog ('Checking BIM Structure...\n')
- s = Arch.makeStructure(length=2,width=3,height=5)
- self.assertTrue(s,"BIM Structure failed")
-
- def testAxis(self):
- App.Console.PrintLog ('Checking Arch Axis...\n')
- a = Arch.makeAxis()
- self.assertTrue(a,"Arch Axis failed")
-
- def testSection(self):
- App.Console.PrintLog ('Checking Arch Section...\n')
- s = Arch.makeSectionPlane([])
- self.assertTrue(s,"Arch Section failed")
-
- def testStairs(self):
- App.Console.PrintLog ('Checking Arch Stairs...\n')
- s = Arch.makeStairs()
- self.assertTrue(s,"Arch Stairs failed")
-
- def testFrame(self):
- App.Console.PrintLog ('Checking Arch Frame...\n')
- l=Draft.makeLine(App.Vector(0,0,0),App.Vector(-2,0,0))
- p = Draft.makeRectangle(length=.5,height=.5)
- f = Arch.makeFrame(l,p)
- self.assertTrue(f,"Arch Frame failed")
-
- def testEquipment(self):
- App.Console.PrintLog ('Checking Arch Equipment...\n')
- box = App.ActiveDocument.addObject("Part::Box", "Box")
- box.Length = 500
- box.Width = 2000
- box.Height = 600
- equip = Arch.makeEquipment(box)
- self.assertTrue(equip,"Arch Equipment failed")
-
- def testPipe(self):
- App.Console.PrintLog ('Checking Arch Pipe...\n')
- pipe = Arch.makePipe(diameter=120, length=3000)
- self.assertTrue(pipe,"Arch Pipe failed")
-
- def testAdd(self):
- App.Console.PrintLog ('Checking Arch Add...\n')
- l=Draft.makeLine(App.Vector(0,0,0),App.Vector(2,0,0))
- w = Arch.makeWall(l,width=0.2,height=2)
- sb = Part.makeBox(1,1,1)
- b = App.ActiveDocument.addObject('Part::Feature','Box')
- b.Shape = sb
- App.ActiveDocument.recompute()
- Arch.addComponents(b,w)
- App.ActiveDocument.recompute()
- r = (w.Shape.Volume > 1.5)
- self.assertTrue(r,"Arch Add failed")
-
- def testRemove(self):
- App.Console.PrintLog ('Checking Arch Remove...\n')
- l=Draft.makeLine(App.Vector(0,0,0),App.Vector(2,0,0))
- w = Arch.makeWall(l,width=0.2,height=2,align="Right")
- sb = Part.makeBox(1,1,1)
- b = App.ActiveDocument.addObject('Part::Feature','Box')
- b.Shape = sb
- App.ActiveDocument.recompute()
- Arch.removeComponents(b,w)
- App.ActiveDocument.recompute()
- r = (w.Shape.Volume < 0.75)
- self.assertTrue(r,"Arch Remove failed")
-
- def testViewGeneration(self):
- """Tests the whole TD view generation workflow"""
-
- operation = "View generation"
- _msg(" Test '{}'".format(operation))
-
- # Create a few objects
- points = [App.Vector(0.0, 0.0, 0.0), App.Vector(2000.0, 0.0, 0.0)]
- line = Draft.make_wire(points)
- wall = Arch.makeWall(line, height=2000)
- wpl = App.Placement(App.Vector(500,0,1500), App.Vector(1,0,0),-90)
- win = Arch.makeWindowPreset('Fixed', width=1000.0, height=1000.0, h1=50.0, h2=50.0, h3=50.0, w1=100.0, w2=50.0, o1=0.0, o2=50.0, placement=wpl)
- win.Hosts = [wall]
- profile = Arch.makeProfile([169, 'HEA', 'HEA100', 'H', 100.0, 96.0, 5.0, 8.0])
- column = Arch.makeStructure(profile, height=2000.0)
- column.Profile = "HEA100"
- column.Placement.Base = App.Vector(500.0, 600.0, 0.0)
- level = Arch.makeFloor()
- level.addObjects([wall, column])
- App.ActiveDocument.recompute()
-
- # Create a drawing view
- section = Arch.makeSectionPlane(level)
- drawing = Arch.make2DDrawing()
- view = Draft.make_shape2dview(section)
- cut = Draft.make_shape2dview(section)
- cut.InPlace = False
- cut.ProjectionMode = "Cutfaces"
- drawing.addObjects([view, cut])
- App.ActiveDocument.recompute()
-
- # Create a TD page
- tpath = os.path.join(App.getResourceDir(),"Mod","TechDraw","Templates","ISO","A3_Landscape_blank.svg")
- page = App.ActiveDocument.addObject("TechDraw::DrawPage", "Page")
- template = App.ActiveDocument.addObject("TechDraw::DrawSVGTemplate", "Template")
- template.Template = tpath
- page.Template = template
- view = App.ActiveDocument.addObject("TechDraw::DrawViewDraft", "DraftView")
- view.Source = drawing
- page.addView(view)
- view.Scale = 1.0
- view.X = "20cm"
- view.Y = "15cm"
- App.ActiveDocument.recompute()
- assert True
diff --git a/src/Mod/BIM/bimtests/TestArchBase.py b/src/Mod/BIM/bimtests/TestArchBase.py
index 72292dbe59..db997cdde5 100644
--- a/src/Mod/BIM/bimtests/TestArchBase.py
+++ b/src/Mod/BIM/bimtests/TestArchBase.py
@@ -30,11 +30,35 @@ import FreeCAD
class TestArchBase(unittest.TestCase):
def setUp(self):
- print(f"Initializing: {self.__class__.__name__}")
- self.document = FreeCAD.newDocument(self.__class__.__name__)
+ """
+ Set up a new, clean document for the test. Ensure test isolation by creating a
+ uniquely-named document and cleaning up any potential leftovers from a previously failed
+ run.
+ """
+ self.doc_name = self.__class__.__name__
+
+ # Close any document of the same name that might have been left over from a crashed or
+ # aborted test run. FreeCAD.getDocument() raises a NameError if the document is not found,
+ # so we wrap this check in a try...except block.
+ try:
+ FreeCAD.getDocument(self.doc_name)
+ # If getDocument() succeeds, the document exists and must be closed.
+ FreeCAD.closeDocument(self.doc_name)
+ except NameError:
+ # This is the expected path on a clean run; do nothing.
+ pass
+
+ # Create a fresh document for the current test.
+ self.document = FreeCAD.newDocument(self.doc_name)
+ self.assertEqual(self.document.Name, self.doc_name)
def tearDown(self):
- FreeCAD.closeDocument(self.document.Name)
+ """Close the test document after all tests in the class are complete."""
+ if hasattr(self, 'document') and self.document:
+ try:
+ FreeCAD.closeDocument(self.document.Name)
+ except Exception as e:
+ FreeCAD.Console.PrintError(f"Error during tearDown in {self.__class__.__name__}: {e}\n")
def printTestMessage(self, text, prepend_text="Test ", end="\n"):
"""Write messages to the console including the line ending.
@@ -43,3 +67,4 @@ class TestArchBase(unittest.TestCase):
passed as the prepend_text argument
"""
FreeCAD.Console.PrintMessage(prepend_text + text + end)
+
diff --git a/src/Mod/BIM/bimtests/TestArchBaseGui.py b/src/Mod/BIM/bimtests/TestArchBaseGui.py
new file mode 100644
index 0000000000..488a3e1ade
--- /dev/null
+++ b/src/Mod/BIM/bimtests/TestArchBaseGui.py
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+# ***************************************************************************
+# * *
+# * Copyright (c) 2025 Furgo *
+# * *
+# * This file is part of FreeCAD. *
+# * *
+# * FreeCAD is free software: you can redistribute it and/or modify it *
+# * under the terms of the GNU Lesser General Public License as *
+# * published by the Free Software Foundation, either version 2.1 of the *
+# * License, or (at your option) any later version. *
+# * *
+# * 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 *
+# * Lesser General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Lesser General Public *
+# * License along with FreeCAD. If not, see *
+# * . *
+# * *
+# ***************************************************************************
+
+import unittest
+import FreeCAD
+import FreeCADGui
+from bimtests.TestArchBase import TestArchBase
+
+class TestArchBaseGui(TestArchBase):
+ """
+ The base class for all Arch/BIM GUI unit tests.
+ It inherits from TestArchBase to handle document setup and adds
+ GUI-specific initialization by activating the BIM workbench.
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ """
+ Ensure the GUI is available and activate the BIM workbench once
+ before any tests in the inheriting class are run.
+ """
+ if not FreeCAD.GuiUp:
+ raise unittest.SkipTest("Cannot run GUI tests in a CLI environment.")
+
+ # Activating the workbench ensures all GUI commands are loaded and ready.
+ FreeCADGui.activateWorkbench("BIMWorkbench")
+
+ def setUp(self):
+ """
+ Run the parent's setup to create the uniquely named document.
+ The workbench is already activated by setUpClass.
+ """
+ super().setUp()
+
diff --git a/src/Mod/BIM/bimtests/TestArchBuildingPartGui.py b/src/Mod/BIM/bimtests/TestArchBuildingPartGui.py
new file mode 100644
index 0000000000..fb978c5766
--- /dev/null
+++ b/src/Mod/BIM/bimtests/TestArchBuildingPartGui.py
@@ -0,0 +1,79 @@
+import FreeCAD as App
+import FreeCADGui
+import Arch
+import Draft
+import Part
+import Sketcher
+from bimtests.TestArchBaseGui import TestArchBaseGui
+
+class TestArchBuildingPartGui(TestArchBaseGui):
+
+ def testBuildingPart(self):
+ """Create a BuildingPart from a wall with a window and check its shape.
+ """
+ # Also regression test for:
+ # https://github.com/FreeCAD/FreeCAD/issues/6178
+ #operation = "Arch BuildingPart"
+ #_msg(" Test '{}'".format(operation))
+ # Most of the code below taken from testWindow function.
+ line = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(3000, 0, 0))
+ wall = Arch.makeWall(line)
+ sk = App.ActiveDocument.addObject("Sketcher::SketchObject", "Sketch001")
+ sk.Placement.Rotation = App.Rotation(App.Vector(1, 0, 0), 90)
+ sk.addGeometry(Part.LineSegment(App.Vector( 500, 800, 0), App.Vector(1500, 800, 0)))
+ sk.addGeometry(Part.LineSegment(App.Vector(1500, 800, 0), App.Vector(1500, 2000, 0)))
+ sk.addGeometry(Part.LineSegment(App.Vector(1500, 2000, 0), App.Vector( 500, 2000, 0)))
+ sk.addGeometry(Part.LineSegment(App.Vector( 500, 2000, 0), App.Vector( 500, 800, 0)))
+ sk.addConstraint(Sketcher.Constraint('Coincident', 0, 2, 1, 1))
+ sk.addConstraint(Sketcher.Constraint('Coincident', 1, 2, 2, 1))
+ sk.addConstraint(Sketcher.Constraint('Coincident', 2, 2, 3, 1))
+ sk.addConstraint(Sketcher.Constraint('Coincident', 3, 2, 0, 1))
+ App.ActiveDocument.recompute()
+ win = Arch.makeWindow(sk)
+ Arch.removeComponents(win, host=wall)
+ App.ActiveDocument.recompute()
+ bp = Arch.makeBuildingPart()
+
+ # Wall visibility works when standalone
+ FreeCADGui.Selection.clearSelection()
+ FreeCADGui.Selection.addSelection(self.doc_name,wall.Name)
+ assert wall.Visibility
+ FreeCADGui.runCommand('Std_ToggleVisibility',0)
+ App.ActiveDocument.recompute()
+ assert not wall.Visibility
+ FreeCADGui.runCommand('Std_ToggleVisibility',0)
+ assert wall.Visibility
+
+ bp.Group = [wall]
+ App.ActiveDocument.recompute()
+ # Fails with OCC 7.5
+ # self.assertTrue(len(bp.Shape.Faces) == 16, "'{}' failed".format(operation))
+
+ # Wall visibility works when inside a BuildingPart
+ FreeCADGui.runCommand('Std_ToggleVisibility',0)
+ App.ActiveDocument.recompute()
+ assert not wall.Visibility
+ FreeCADGui.runCommand('Std_ToggleVisibility',0)
+ assert wall.Visibility
+
+ # Wall visibility works when BuildingPart Toggled
+ FreeCADGui.Selection.clearSelection()
+ FreeCADGui.Selection.addSelection(self.doc_name,bp.Name)
+ FreeCADGui.runCommand('Std_ToggleVisibility',0)
+ assert not wall.Visibility
+ FreeCADGui.runCommand('Std_ToggleVisibility',0)
+ assert wall.Visibility
+
+ # Wall visibiity works inside group inside BuildingPart Toggled
+ grp = App.ActiveDocument.addObject("App::DocumentObjectGroup","Group")
+ grp.Label="Group"
+ grp.Group = [wall]
+ bp.Group = [grp]
+ App.ActiveDocument.recompute()
+ assert wall.Visibility
+ FreeCADGui.runCommand('Std_ToggleVisibility',0)
+ App.ActiveDocument.recompute()
+ assert not wall.Visibility
+ FreeCADGui.runCommand('Std_ToggleVisibility',0)
+ App.ActiveDocument.recompute()
+ assert wall.Visibility
\ No newline at end of file
diff --git a/src/Mod/BIM/bimtests/TestArchComponent.py b/src/Mod/BIM/bimtests/TestArchComponent.py
index cbb82b0188..81b50812bf 100644
--- a/src/Mod/BIM/bimtests/TestArchComponent.py
+++ b/src/Mod/BIM/bimtests/TestArchComponent.py
@@ -26,6 +26,7 @@
import Arch
import Draft
+import Part
import FreeCAD as App
from bimtests import TestArchBase
from draftutils.messages import _msg
@@ -34,6 +35,32 @@ from math import pi, cos, sin, radians
class TestArchComponent(TestArchBase.TestArchBase):
+ def testAdd(self):
+ App.Console.PrintLog ('Checking Arch Add...\n')
+ l=Draft.makeLine(App.Vector(0,0,0),App.Vector(2,0,0))
+ w = Arch.makeWall(l,width=0.2,height=2)
+ sb = Part.makeBox(1,1,1)
+ b = App.ActiveDocument.addObject('Part::Feature','Box')
+ b.Shape = sb
+ App.ActiveDocument.recompute()
+ Arch.addComponents(b,w)
+ App.ActiveDocument.recompute()
+ r = (w.Shape.Volume > 1.5)
+ self.assertTrue(r,"Arch Add failed")
+
+ def testRemove(self):
+ App.Console.PrintLog ('Checking Arch Remove...\n')
+ l=Draft.makeLine(App.Vector(0,0,0),App.Vector(2,0,0))
+ w = Arch.makeWall(l,width=0.2,height=2,align="Right")
+ sb = Part.makeBox(1,1,1)
+ b = App.ActiveDocument.addObject('Part::Feature','Box')
+ b.Shape = sb
+ App.ActiveDocument.recompute()
+ Arch.removeComponents(b,w)
+ App.ActiveDocument.recompute()
+ r = (w.Shape.Volume < 0.75)
+ self.assertTrue(r,"Arch Remove failed")
+
def testBsplineSlabAreas(self):
"""Test the HorizontalArea and VerticalArea properties of a Bspline-based slab.
diff --git a/src/Mod/BIM/bimtests/TestArchEquipment.py b/src/Mod/BIM/bimtests/TestArchEquipment.py
index fde7d625ba..22d5f57f0f 100644
--- a/src/Mod/BIM/bimtests/TestArchEquipment.py
+++ b/src/Mod/BIM/bimtests/TestArchEquipment.py
@@ -23,15 +23,22 @@
# ***************************************************************************
import Arch
+import FreeCAD as App
from bimtests import TestArchBase
class TestArchEquipment(TestArchBase.TestArchBase):
def test_makeEquipment(self):
"""Test the makeEquipment function."""
- operation = "Testing makeEquipment..."
- self.printTestMessage(operation)
obj = Arch.makeEquipment()
self.assertIsNotNone(obj, "makeEquipment failed to create an object")
- self.assertEqual(obj.Label, "Equipment", "Incorrect default label for Equipment")
\ No newline at end of file
+ self.assertEqual(obj.Label, "Equipment", "Incorrect default label for Equipment")
+
+ def testEquipment(self):
+ box = App.ActiveDocument.addObject("Part::Box", "Box")
+ box.Length = 500
+ box.Width = 2000
+ box.Height = 600
+ equip = Arch.makeEquipment(box)
+ self.assertTrue(equip,"Arch Equipment failed")
\ No newline at end of file
diff --git a/src/Mod/BIM/bimtests/TestArchFrame.py b/src/Mod/BIM/bimtests/TestArchFrame.py
index cc12facba1..ea83420334 100644
--- a/src/Mod/BIM/bimtests/TestArchFrame.py
+++ b/src/Mod/BIM/bimtests/TestArchFrame.py
@@ -23,15 +23,21 @@
# ***************************************************************************
import Arch
+import Draft
+import FreeCAD as App
from bimtests import TestArchBase
class TestArchFrame(TestArchBase.TestArchBase):
def test_makeFrame(self):
"""Test the makeFrame function."""
- operation = "Testing makeFrame..."
- self.printTestMessage(operation)
obj = Arch.makeFrame(None, None)
self.assertIsNotNone(obj, "makeFrame failed to create an object")
- self.assertEqual(obj.Label, "Frame", "Incorrect default label for Frame")
\ No newline at end of file
+ self.assertEqual(obj.Label, "Frame", "Incorrect default label for Frame")
+
+ def testFrame(self):
+ l=Draft.makeLine(App.Vector(0,0,0),App.Vector(-2,0,0))
+ p = Draft.makeRectangle(length=.5,height=.5)
+ f = Arch.makeFrame(l,p)
+ self.assertTrue(f,"Arch Frame failed")
\ No newline at end of file
diff --git a/src/Mod/BIM/bimtests/TestArchImportersGui.py b/src/Mod/BIM/bimtests/TestArchImportersGui.py
new file mode 100644
index 0000000000..7337972151
--- /dev/null
+++ b/src/Mod/BIM/bimtests/TestArchImportersGui.py
@@ -0,0 +1,88 @@
+
+import FreeCAD as App
+from bimtests.TestArchBaseGui import TestArchBaseGui
+
+class TestArchImportersGui(TestArchBaseGui):
+
+ def testImportSH3D(self):
+ """Import a SweetHome 3D file
+ """
+ import BIM.importers.importSH3DHelper
+
+ importer = BIM.importers.importSH3DHelper.SH3DImporter(None)
+ importer.import_sh3d_from_string(SH3D_HOME)
+
+ assert App.ActiveDocument.Site
+ assert App.ActiveDocument.BuildingPart.Label == "Building"
+ assert App.ActiveDocument.BuildingPart001.Label == "Level"
+ assert App.ActiveDocument.Wall
+
+
+SH3D_HOME = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
diff --git a/src/Mod/BIM/bimtests/TestArchRebar.py b/src/Mod/BIM/bimtests/TestArchRebar.py
index d981207cc5..5253804f25 100644
--- a/src/Mod/BIM/bimtests/TestArchRebar.py
+++ b/src/Mod/BIM/bimtests/TestArchRebar.py
@@ -170,4 +170,27 @@ class TestArchRebar(TestArchBase.TestArchBase):
# The Shape should be the default null/empty shape.
self.assertIsNotNone(rebar.Shape, "Rebar object should always have a Shape attribute.")
self.assertTrue(rebar.Shape.isNull(),
- "Rebar.Shape should be a null shape if no Base is provided.")
\ No newline at end of file
+ "Rebar.Shape should be a null shape if no Base is provided.")
+
+ def test_RebarMe(self):
+ """
+ Tests the creation of an Arch Rebar
+ """
+ import Sketcher
+ self.printTestMessage('Checking Arch Rebar...\n')
+ s = Arch.makeStructure(length=2, width=3, height=5)
+ sk = self.document.addObject('Sketcher::SketchObject', 'Sketch')
+ sk.AttachmentSupport = (s, ["Face6"])
+ sk.addGeometry(Part.LineSegment(FreeCAD.Vector(-0.85, 1.25, 0), FreeCAD.Vector(0.75, 1.25, 0)))
+ sk.addGeometry(Part.LineSegment(FreeCAD.Vector(0.75, 1.25, 0), FreeCAD.Vector(0.75, -1.20, 0)))
+ sk.addGeometry(Part.LineSegment(FreeCAD.Vector(0.75, -1.20, 0), FreeCAD.Vector(-0.85, -1.20, 0)))
+ sk.addGeometry(Part.LineSegment(FreeCAD.Vector(-0.85, -1.20, 0), FreeCAD.Vector(-0.85, 1.25, 0)))
+ sk.addConstraint(Sketcher.Constraint('Coincident', 0, 2, 1, 1))
+ sk.addConstraint(Sketcher.Constraint('Coincident', 1, 2, 2, 1))
+ sk.addConstraint(Sketcher.Constraint('Coincident', 2, 2, 3, 1))
+ sk.addConstraint(Sketcher.Constraint('Coincident', 3, 2, 0, 1))
+ self.document.recompute()
+ r = Arch.makeRebar(s, sk, diameter=.1, amount=2)
+ self.document.recompute()
+ self.assertTrue(r, "Arch Rebar creation failed")
+ self.assertFalse(r.Shape.isNull(), "Rebar shape is null")
\ No newline at end of file
diff --git a/src/Mod/BIM/bimtests/TestArchSectionPlane.py b/src/Mod/BIM/bimtests/TestArchSectionPlane.py
index 07871f3524..d40aa5c2c3 100644
--- a/src/Mod/BIM/bimtests/TestArchSectionPlane.py
+++ b/src/Mod/BIM/bimtests/TestArchSectionPlane.py
@@ -23,6 +23,9 @@
# ***************************************************************************
import Arch
+import Draft
+import os
+import FreeCAD as App
from bimtests import TestArchBase
class TestArchSectionPlane(TestArchBase.TestArchBase):
@@ -34,4 +37,47 @@ class TestArchSectionPlane(TestArchBase.TestArchBase):
section_plane = Arch.makeSectionPlane(name="TestSectionPlane")
self.assertIsNotNone(section_plane, "makeSectionPlane failed to create a section plane object.")
- self.assertEqual(section_plane.Label, "TestSectionPlane", "Section plane label is incorrect.")
\ No newline at end of file
+ self.assertEqual(section_plane.Label, "TestSectionPlane", "Section plane label is incorrect.")
+
+ def testViewGeneration(self):
+ """Tests the whole TD view generation workflow"""
+
+ # Create a few objects
+ points = [App.Vector(0.0, 0.0, 0.0), App.Vector(2000.0, 0.0, 0.0)]
+ line = Draft.make_wire(points)
+ wall = Arch.makeWall(line, height=2000)
+ wpl = App.Placement(App.Vector(500,0,1500), App.Vector(1,0,0),-90)
+ win = Arch.makeWindowPreset('Fixed', width=1000.0, height=1000.0, h1=50.0, h2=50.0, h3=50.0, w1=100.0, w2=50.0, o1=0.0, o2=50.0, placement=wpl)
+ win.Hosts = [wall]
+ profile = Arch.makeProfile([169, 'HEA', 'HEA100', 'H', 100.0, 96.0, 5.0, 8.0])
+ column = Arch.makeStructure(profile, height=2000.0)
+ column.Profile = "HEA100"
+ column.Placement.Base = App.Vector(500.0, 600.0, 0.0)
+ level = Arch.makeFloor()
+ level.addObjects([wall, column])
+ App.ActiveDocument.recompute()
+
+ # Create a drawing view
+ section = Arch.makeSectionPlane(level)
+ drawing = Arch.make2DDrawing()
+ view = Draft.make_shape2dview(section)
+ cut = Draft.make_shape2dview(section)
+ cut.InPlace = False
+ cut.ProjectionMode = "Cutfaces"
+ drawing.addObjects([view, cut])
+ App.ActiveDocument.recompute()
+
+ # Create a TD page
+ tpath = os.path.join(App.getResourceDir(),"Mod","TechDraw","Templates","ISO","A3_Landscape_blank.svg")
+ page = App.ActiveDocument.addObject("TechDraw::DrawPage", "Page")
+ template = App.ActiveDocument.addObject("TechDraw::DrawSVGTemplate", "Template")
+ template.Template = tpath
+ page.Template = template
+ view = App.ActiveDocument.addObject("TechDraw::DrawViewDraft", "DraftView")
+ view.Source = drawing
+ page.addView(view)
+ view.Scale = 1.0
+ view.X = "20cm"
+ view.Y = "15cm"
+ App.ActiveDocument.recompute()
+ assert True
\ No newline at end of file
diff --git a/src/Mod/BIM/bimtests/TestArchStructure.py b/src/Mod/BIM/bimtests/TestArchStructure.py
new file mode 100644
index 0000000000..488b3fb973
--- /dev/null
+++ b/src/Mod/BIM/bimtests/TestArchStructure.py
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+# ***************************************************************************
+# * *
+# * Copyright (c) 2025 Furgo *
+# * *
+# * This file is part of FreeCAD. *
+# * *
+# * FreeCAD is free software: you can redistribute it and/or modify it *
+# * under the terms of the GNU Lesser General Public License as *
+# * published by the Free Software Foundation, either version 2.1 of the *
+# * License, or (at your option) any later version. *
+# * *
+# * 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 *
+# * Lesser General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Lesser General Public *
+# * License along with FreeCAD. If not, see *
+# * . *
+# * *
+# ***************************************************************************
+
+import FreeCAD as App
+import Arch
+from bimtests import TestArchBase
+
+class TestArchStructure(TestArchBase.TestArchBase):
+
+ def testStructure(self):
+ App.Console.PrintLog ('Checking BIM Structure...\n')
+ s = Arch.makeStructure(length=2,width=3,height=5)
+ self.assertTrue(s,"BIM Structure failed")
\ No newline at end of file
diff --git a/src/Mod/BIM/bimtests/TestArchWindow.py b/src/Mod/BIM/bimtests/TestArchWindow.py
index 16efeac801..1a6f3ab96a 100644
--- a/src/Mod/BIM/bimtests/TestArchWindow.py
+++ b/src/Mod/BIM/bimtests/TestArchWindow.py
@@ -309,34 +309,39 @@ class TestArchWindow(TestArchBase.TestArchBase):
def test_create_window_on_xz_plane(self):
"""Test creating a window oriented on the XZ (vertical) plane."""
- sketch = self._create_sketch_with_wires("Sketch_XZ_Plane",
- [(0,0,1000,1200), (100,100,800,1000)])
- sketch.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1,0,0), 90)
+ # Create a a frame-like profile.
+ sketch = self._create_sketch_with_wires("Sketch_XZ_Plane",
+ [(0, 0, 1000, 1200), (100, 100, 800, 1000)])
+
+ # Orient the sketch to the XZ plane.
+ sketch.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), 90)
self.document.recompute()
+ # Create the window using explicit parts.
window = Arch.makeWindow(baseobj=sketch, name="Window_XZ_Plane")
window.WindowParts = [
- "Frame", "Frame", "Wire0,Wire1", "60", "0",
- "Glass", "Glass panel", "Wire1", "10", "25"
+ "Frame", "Frame", "Wire0,Wire1", "60", "0", # A 60mm thick frame
+ "Glass", "Glass panel", "Wire1", "10", "25" # A 10mm thick glass pane, offset by 25mm
]
self.document.recompute()
- self.assertFalse(window.Shape.isNull())
- self.assertGreater(len(window.Shape.Solids), 0)
-
- expected_normal_y = 1.0
-
- self.assertAlmostEqual(window.Normal.x, 0.0, places=5)
- self.assertAlmostEqual(window.Normal.y, expected_normal_y, places=5,
- msg=f"Window normal Y component incorrect. Expected approx {expected_normal_y}, got {window.Normal.y}")
- self.assertAlmostEqual(window.Normal.z, 0.0, places=5)
+ # Check the resulting geometry's orientation and dimensions.
+ self.assertFalse(window.Shape.isNull(), "Window shape should not be null.")
+ self.assertEqual(len(window.Shape.Solids), 2, "Window should contain two solids (frame and glass).")
bb = window.Shape.BoundBox
+
+ # The window's overall "thickness" is determined by the frame (60mm).
+ # Its "width" (X) and "height" (Z) should be larger.
self.assertGreater(bb.XLength, bb.YLength,
- "Window XLength (width) should be greater than YLength (thickness).")
+ "Window XLength (width) should be greater than YLength (thickness).")
self.assertGreater(bb.ZLength, bb.YLength,
- "Window ZLength (height) should be greater than YLength (thickness).")
+ "Window ZLength (height) should be greater than YLength (thickness).")
+
+ # Verify the overall thickness is correct (60mm, from the Frame component).
+ self.assertAlmostEqual(bb.YLength, 60.0, places=5,
+ msg="Window thickness (YLength) is incorrect.")
def test_opening_property_rotates_component(self):
"""Test that setting the Opening property rotates a hinged component."""
@@ -559,7 +564,7 @@ class TestArchWindow(TestArchBase.TestArchBase):
Helper to create a sketch with one or more specified rectangular wires.
Each rectangle is defined in the sketch's local XY plane. The sketch
- is created in the current document (`self.document`). After adding all
+ is created in the current document (`self.doc`). After adding all
geometry and basic coincident constraints for each rectangle, the
document is recomputed.
@@ -608,7 +613,7 @@ class TestArchWindow(TestArchBase.TestArchBase):
"""
Helper to create a rectangular sketch with "Width" and "Height" named constraints.
- The sketch is created in the current document (`self.document`) on the
+ The sketch is created in the current document (`self.doc`) on the
default XY plane. It consists of a single rectangle defined by four
line segments, with its bottom-left corner at (0,0,0) in the
sketch's local coordinates.