| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | """ |
| | Unit tests for CommandInsertLink module. |
| | |
| | This module contains unit tests to verify the proper handling of null |
| | LinkedObject references in the TaskAssemblyInsertLink.accept() method. |
| | Tests ensure that invalid objects are gracefully skipped without causing |
| | AttributeError crashes. |
| | """ |
| |
|
| | import unittest |
| | from unittest.mock import patch, MagicMock |
| |
|
| | import FreeCAD as App |
| |
|
| | |
| | if App.GuiUp: |
| | import CommandInsertLink |
| |
|
| |
|
| | def _msg(text, end="\n"): |
| | """Write messages to the console including the line ending.""" |
| | App.Console.PrintMessage(text + end) |
| |
|
| |
|
| | @unittest.skipIf(not App.GuiUp, "GUI tests require FreeCAD GUI mode") |
| | class TestCommandInsertLink(unittest.TestCase): |
| | """Unit tests for CommandInsertLink module.""" |
| |
|
| | @classmethod |
| | def setUpClass(cls): |
| | """setUpClass()... |
| | This method is called upon instantiation of this test class. Add code and objects here |
| | that are needed for the duration of the test() methods in this class. In other words, |
| | set up the 'global' test environment here; use the `setUp()` method to set up a 'local' |
| | test environment. |
| | This method does not have access to the class `self` reference, but it |
| | is able to call static methods within this same class. |
| | """ |
| |
|
| | @classmethod |
| | def tearDownClass(cls): |
| | """tearDownClass()... |
| | This method is called prior to destruction of this test class. Add code and objects here |
| | that cleanup the test environment after the test() methods in this class have been executed. |
| | This method does not have access to the class `self` reference. This method |
| | is able to call static methods within this same class. |
| | """ |
| |
|
| | 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. |
| | """ |
| | 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") |
| |
|
| | _msg(f" Temporary document '{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. |
| | """ |
| | App.closeDocument(self.doc.Name) |
| |
|
| | @patch("FreeCADGui.PySideUic.loadUi") |
| | @patch("CommandInsertLink.TaskAssemblyInsertLink.adjustTreeWidgetSize") |
| | def test_mixed_valid_and_invalid_objects(self, _mock_adjustTreeSize, _mock_loadUi): |
| | """Test that accept() handles a mix of valid and invalid objects correctly.""" |
| | operation = "Handle mixed valid/invalid objects" |
| | _msg(f" Test '{operation}'") |
| |
|
| | mock_view = MagicMock() |
| | mock_view.getSize = MagicMock(return_value=(800, 600)) |
| | task = CommandInsertLink.TaskAssemblyInsertLink(self.assembly, mock_view) |
| |
|
| | |
| | test_objects = [ |
| | |
| | { |
| | "addedObject": type( |
| | "ValidObject", |
| | (), |
| | { |
| | "Name": "ValidObject", |
| | "Label": "Valid Object", |
| | "LinkedObject": type( |
| | "ValidLinkedObject", (), {"Name": "ValidLinkedObjectName"} |
| | )(), |
| | }, |
| | )(), |
| | "translation": App.Vector(1, 1, 1), |
| | }, |
| | |
| | { |
| | "addedObject": type( |
| | "InvalidObject1", |
| | (), |
| | {"Name": "InvalidObject1", "Label": "Invalid Object 1", "LinkedObject": None}, |
| | )(), |
| | "translation": App.Vector(2, 2, 2), |
| | }, |
| | |
| | { |
| | "addedObject": type( |
| | "InvalidObject2", (), {"Name": "InvalidObject2", "Label": "Invalid Object 2"} |
| | )(), |
| | "translation": App.Vector(3, 3, 3), |
| | }, |
| | |
| | { |
| | "addedObject": type( |
| | "InvalidObject3", |
| | (), |
| | { |
| | "Name": "InvalidObject3", |
| | "Label": "Invalid Object 3", |
| | "LinkedObject": type("LinkedObjectWithNoneName", (), {"Name": None})(), |
| | }, |
| | )(), |
| | "translation": App.Vector(4, 4, 4), |
| | }, |
| | |
| | { |
| | "addedObject": type( |
| | "InvalidObject4", |
| | (), |
| | { |
| | "Label": "Invalid Object 4", |
| | "LinkedObject": type("LinkedObject", (), {"Name": "Something"})(), |
| | }, |
| | )(), |
| | "translation": App.Vector(5, 5, 5), |
| | }, |
| | ] |
| |
|
| | |
| | for obj_data in test_objects: |
| | task.insertionStack.append(obj_data) |
| |
|
| | |
| | result = task.accept() |
| | self.assertTrue(result, "accept() should return True even with mixed valid/invalid objects") |
| | _msg(" Successfully handled mixed valid/invalid objects") |
| |
|
| | @patch("FreeCADGui.PySideUic.loadUi") |
| | @patch("CommandInsertLink.TaskAssemblyInsertLink.adjustTreeWidgetSize") |
| | def test_empty_insertion_stack(self, _mock_adjustTreeSize, _mock_ui): |
| | """Test that accept() handles empty insertion stack correctly.""" |
| | operation = "Handle empty insertion stack" |
| | _msg(f" Test '{operation}'") |
| |
|
| | mock_view = MagicMock() |
| | mock_view.getSize = MagicMock(return_value=(800, 600)) |
| | task = CommandInsertLink.TaskAssemblyInsertLink(self.assembly, mock_view) |
| |
|
| | |
| | self.assertEqual(len(task.insertionStack), 0, "Insertion stack should be empty") |
| |
|
| | result = task.accept() |
| | self.assertTrue(result, "accept() should return True even with empty insertion stack") |
| | _msg(" Successfully handled empty insertion stack") |
| |
|