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.