From 1062ea4eb04bf62f8919f38e98a7c1918f6527b9 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Wed, 24 Jan 2024 18:42:47 +0100 Subject: [PATCH] AssemblyTest: Introduce assembly test module --- src/Mod/Assembly/AssemblyTests/TestCore.py | 169 ++++++++++++++++-- src/Mod/Assembly/TestAssemblyWorkbench.py | 5 +- tests/CMakeLists.txt | 3 + tests/src/Mod/Assembly/App/AssemblyObject.cpp | 57 ++++++ tests/src/Mod/Assembly/App/CMakeLists.txt | 5 + tests/src/Mod/Assembly/CMakeLists.txt | 15 ++ tests/src/Mod/CMakeLists.txt | 3 + 7 files changed, 244 insertions(+), 13 deletions(-) create mode 100644 tests/src/Mod/Assembly/App/AssemblyObject.cpp create mode 100644 tests/src/Mod/Assembly/App/CMakeLists.txt create mode 100644 tests/src/Mod/Assembly/CMakeLists.txt diff --git a/src/Mod/Assembly/AssemblyTests/TestCore.py b/src/Mod/Assembly/AssemblyTests/TestCore.py index e2e3796733..44bab3adfc 100644 --- a/src/Mod/Assembly/AssemblyTests/TestCore.py +++ b/src/Mod/Assembly/AssemblyTests/TestCore.py @@ -21,10 +21,18 @@ # * # ***************************************************************************/ -import FreeCAD +import FreeCAD as App import Part import unittest +import UtilsAssembly +import JointObject + + +def _msg(text, end="\n"): + """Write messages to the console including the line ending.""" + App.Console.PrintMessage(text + end) + class TestCore(unittest.TestCase): @classmethod @@ -49,26 +57,167 @@ class TestCore(unittest.TestCase): """ pass - # Close geometry document without saving - # FreeCAD.closeDocument(FreeCAD.ActiveDocument.Name) - # Setup and tear down methods called before and after each unit test def setUp(self): """setUp()... This method is called prior to each `test()` method. Add code and objects here that are needed for multiple `test()` methods. """ - self.doc = FreeCAD.ActiveDocument - self.con = FreeCAD.Console + doc_name = self.__class__.__name__ + if App.ActiveDocument: + if App.ActiveDocument.Name != doc_name: + App.newDocument(doc_name) + else: + App.newDocument(doc_name) + App.setActiveDocument(doc_name) + self.doc = App.ActiveDocument + + self.assembly = App.ActiveDocument.addObject("Assembly::AssemblyObject", "Assembly") + if self.assembly: + self.jointgroup = self.assembly.newObject("Assembly::JointGroup", "Joints") + + _msg(" Temporary document '{}'".format(self.doc.Name)) def tearDown(self): """tearDown()... This method is called after each test() method. Add cleanup instructions here. Such cleanup instructions will likely undo those in the setUp() method. """ - pass + App.closeDocument(self.doc.Name) - def test00(self): - pass + def test_create_assembly(self): + """Create an assembly.""" + operation = "Create Assembly Object" + _msg(" Test '{}'".format(operation)) + self.assertTrue(self.assembly, "'{}' failed".format(operation)) - self.assertTrue(True) + def test_create_jointGroup(self): + """Create a joint group in an assembly.""" + operation = "Create JointGroup Object" + _msg(" Test '{}'".format(operation)) + self.assertTrue(self.jointgroup, "'{}' failed".format(operation)) + + def test_create_joint(self): + """Create a joint in an assembly.""" + operation = "Create Joint Object" + _msg(" Test '{}'".format(operation)) + + joint = self.jointgroup.newObject("App::FeaturePython", "testJoint") + self.assertTrue(joint, "'{}' failed (FeaturePython creation failed)".format(operation)) + JointObject.Joint(joint, 0) + + self.assertTrue(hasattr(joint, "JointType"), "'{}' failed".format(operation)) + + def test_create_grounded_joint(self): + """Create a grounded joint in an assembly.""" + operation = "Create Grounded Joint Object" + _msg(" Test '{}'".format(operation)) + + groundedjoint = self.jointgroup.newObject("App::FeaturePython", "testJoint") + self.assertTrue( + groundedjoint, "'{}' failed (FeaturePython creation failed)".format(operation) + ) + + box = self.assembly.newObject("Part::Box", "Box") + + JointObject.GroundedJoint(groundedjoint, box) + + self.assertTrue( + hasattr(groundedjoint, "ObjectToGround"), + "'{}' failed: No attribute 'ObjectToGround'".format(operation), + ) + self.assertTrue( + groundedjoint.ObjectToGround == box, + "'{}' failed: ObjectToGround not set correctly.".format(operation), + ) + + def test_find_placement(self): + """Test find placement of joint.""" + operation = "Find placement" + _msg(" Test '{}'".format(operation)) + + joint = self.jointgroup.newObject("App::FeaturePython", "testJoint") + JointObject.Joint(joint, 0) + + L = 2 + W = 3 + H = 7 + box = self.assembly.newObject("Part::Box", "Box") + box.Length = L + box.Width = W + box.Height = H + box.Placement = App.Placement(App.Vector(10, 20, 30), App.Rotation(15, 25, 35)) + + # Step 0 : box with placement. No element selected + plc = joint.Proxy.findPlacement(joint, box.Name, box, "", "") + targetPlc = App.Placement(App.Vector(), App.Rotation()) + self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 0".format(operation)) + + # Step 1 : box with placement. Face + Vertex + plc = joint.Proxy.findPlacement(joint, box.Name, box, "Face6", "Vertex7") + targetPlc = App.Placement(App.Vector(L, W, H), App.Rotation()) + self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 1".format(operation)) + + # Step 2 : box with placement. Edge + Vertex + plc = joint.Proxy.findPlacement(joint, box.Name, box, "Edge8", "Vertex8") + targetPlc = App.Placement(App.Vector(L, W, 0), App.Rotation(0, 0, -90)) + self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 2".format(operation)) + + # Step 3 : box with placement. Vertex + plc = joint.Proxy.findPlacement(joint, box.Name, box, "Vertex3", "Vertex3") + targetPlc = App.Placement(App.Vector(0, W, H), App.Rotation()) + _msg(" plc '{}'".format(plc)) + _msg(" targetPlc '{}'".format(targetPlc)) + self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 3".format(operation)) + + # Step 4 : box with placement. Face + plc = joint.Proxy.findPlacement(joint, box.Name, box, "Face2", "Face2") + targetPlc = App.Placement(App.Vector(L, W / 2, H / 2), App.Rotation(0, -90, 180)) + _msg(" plc '{}'".format(plc)) + _msg(" targetPlc '{}'".format(targetPlc)) + self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 4".format(operation)) + + def test_solve_assembly(self): + """Test solving an assembly.""" + operation = "Solve assembly" + _msg(" Test '{}'".format(operation)) + + box = self.assembly.newObject("Part::Box", "Box") + box.Length = 10 + box.Width = 10 + box.Height = 10 + box.Placement = App.Placement(App.Vector(10, 20, 30), App.Rotation(15, 25, 35)) + + box2 = self.assembly.newObject("Part::Box", "Box") + box2.Length = 10 + box2.Width = 10 + box2.Height = 10 + box2.Placement = App.Placement(App.Vector(40, 50, 60), App.Rotation(45, 55, 65)) + + ground = self.jointgroup.newObject("App::FeaturePython", "GroundedJoint") + JointObject.GroundedJoint(ground, box2) + + joint = self.jointgroup.newObject("App::FeaturePython", "testJoint") + JointObject.Joint(joint, 0) + + current_selection = [] + current_selection.append( + { + "object": box2, + "part": box2, + "element_name": "Face6", + "vertex_name": "Vertex7", + } + ) + current_selection.append( + { + "object": box, + "part": box, + "element_name": "Face6", + "vertex_name": "Vertex7", + } + ) + + joint.Proxy.setJointConnectors(joint, current_selection) + + self.assertTrue(box.Placement.isSame(box2.Placement, 1e-6), "'{}'".format(operation)) diff --git a/src/Mod/Assembly/TestAssemblyWorkbench.py b/src/Mod/Assembly/TestAssemblyWorkbench.py index 000241de0c..a9d404fd54 100644 --- a/src/Mod/Assembly/TestAssemblyWorkbench.py +++ b/src/Mod/Assembly/TestAssemblyWorkbench.py @@ -26,6 +26,5 @@ import TestApp from AssemblyTests.TestCore import TestCore -# dummy usage to get flake8 and lgtm quiet -False if TestCore.__name__ else True -False if TestApp.__name__ else True +# Use the modules so that code checkers don't complain (flake8) +True if TestCore else False diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f01639d7bd..95758fd826 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -78,6 +78,9 @@ set(TestExecutables Tests_run ) +if(BUILD_ASSEMBLY) + list (APPEND TestExecutables Assembly_tests_run) +endif(BUILD_ASSEMBLY) if(BUILD_MATERIAL) list (APPEND TestExecutables Material_tests_run) endif(BUILD_MATERIAL) diff --git a/tests/src/Mod/Assembly/App/AssemblyObject.cpp b/tests/src/Mod/Assembly/App/AssemblyObject.cpp new file mode 100644 index 0000000000..2f0f86ef95 --- /dev/null +++ b/tests/src/Mod/Assembly/App/AssemblyObject.cpp @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "gtest/gtest.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +class AssemblyObjectTest: public ::testing::Test +{ +protected: + static void SetUpTestSuite() + { + tests::initApplication(); + } + + void SetUp() override + { + _docName = App::GetApplication().getUniqueDocumentName("test"); + auto _doc = App::GetApplication().newDocument(_docName.c_str(), "testUser"); + _assemblyObj = + static_cast(_doc->addObject("Assembly::AssemblyObject")); + _jointGroupObj = static_cast( + _assemblyObj->addObject("Assembly::JointGroup", "jointGroupTest")); + } + + void TearDown() override + { + App::GetApplication().closeDocument(_docName.c_str()); + } + + Assembly::AssemblyObject* getObject() + { + return _assemblyObj; + } + +private: + // TODO: use shared_ptr or something else here? + Assembly::AssemblyObject* _assemblyObj; + Assembly::JointGroup* _jointGroupObj; + std::string _docName; +}; + +TEST_F(AssemblyObjectTest, createAssemblyObject) // NOLINT +{ + // Arrange + + // Act + + // Assert +} diff --git a/tests/src/Mod/Assembly/App/CMakeLists.txt b/tests/src/Mod/Assembly/App/CMakeLists.txt new file mode 100644 index 0000000000..41c6eb9a84 --- /dev/null +++ b/tests/src/Mod/Assembly/App/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources( + Assembly_tests_run + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/AssemblyObject.cpp +) diff --git a/tests/src/Mod/Assembly/CMakeLists.txt b/tests/src/Mod/Assembly/CMakeLists.txt new file mode 100644 index 0000000000..0b6aaf602a --- /dev/null +++ b/tests/src/Mod/Assembly/CMakeLists.txt @@ -0,0 +1,15 @@ + +target_include_directories(Assembly_tests_run PUBLIC + ${EIGEN3_INCLUDE_DIR} + ${OCC_INCLUDE_DIR} + ${Python3_INCLUDE_DIRS} + ${XercesC_INCLUDE_DIRS} +) + +target_link_libraries(Assembly_tests_run + gtest_main + ${Google_Tests_LIBS} + Assembly +) + +add_subdirectory(App) diff --git a/tests/src/Mod/CMakeLists.txt b/tests/src/Mod/CMakeLists.txt index 597d6e878a..05c9bbe6e2 100644 --- a/tests/src/Mod/CMakeLists.txt +++ b/tests/src/Mod/CMakeLists.txt @@ -1,3 +1,6 @@ +if(BUILD_ASSEMBLY) + add_subdirectory(Assembly) +endif(BUILD_ASSEMBLY) if(BUILD_MATERIAL) add_subdirectory(Material) endif(BUILD_MATERIAL)