EntityOutliner_EntityOrdering.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. """
  2. Copyright (c) Contributors to the Open 3D Engine Project.
  3. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. SPDX-License-Identifier: Apache-2.0 OR MIT
  5. """
  6. class Tests:
  7. entities_sorted = (
  8. "Entities sorted in the expected order",
  9. "Entities sorted in an incorrect order",
  10. )
  11. def EntityOutliner_EntityOrdering():
  12. """
  13. Summary:
  14. Verify that manual entity ordering in the entity outliner works and is stable.
  15. Expected Behavior:
  16. Several entities are created, some are manually ordered, and their order
  17. is maintained, even when new entities are added.
  18. Test Steps:
  19. 1) Open the empty Prefab Base level
  20. 2) Add 5 entities to the outliner
  21. 3) Move "Entity1" to the top of the order
  22. 4) Move "Entity4" to the bottom of the order
  23. 5) Add another new entity, ensure the rest of the order is unchanged
  24. """
  25. from PySide2 import QtCore
  26. import azlmbr.legacy.general as general
  27. import editor_python_test_tools.hydra_editor_utils as hydra
  28. import pyside_utils
  29. from editor_python_test_tools.utils import Report
  30. from editor_python_test_tools.wait_utils import PrefabWaiter
  31. # Grab the Editor, Entity Outliner, and Outliner Model
  32. editor_window = pyside_utils.get_editor_main_window()
  33. entity_outliner = pyside_utils.find_child_by_hierarchy(
  34. editor_window, ..., "EntityOutlinerWidgetUI", ..., "m_objectTree"
  35. )
  36. entity_outliner_model = entity_outliner.model()
  37. # Get the outliner index for the root prefab container entity
  38. def get_root_prefab_container_index():
  39. return entity_outliner_model.index(0, 0)
  40. # Get the outliner index for the top level entity of a given name
  41. def index_for_name(name):
  42. root_index = get_root_prefab_container_index()
  43. for row in range(entity_outliner_model.rowCount(root_index)):
  44. row_index = entity_outliner_model.index(row, 0, root_index)
  45. if row_index.data() == name:
  46. return row_index
  47. return None
  48. # Validate that the outliner top level entity order matches the expected order
  49. def verify_entities_sorted(expected_order):
  50. actual_order = []
  51. root_index = get_root_prefab_container_index()
  52. for row in range(entity_outliner_model.rowCount(root_index)):
  53. row_index = entity_outliner_model.index(row, 0, root_index)
  54. actual_order.append(row_index.data())
  55. sorted_correctly = actual_order == expected_order
  56. Report.result(Tests.entities_sorted, sorted_correctly)
  57. if not sorted_correctly:
  58. print(f"Expected entity order: {expected_order}")
  59. print(f"Actual entity order: {actual_order}")
  60. # Creates an entity from the outliner context menu
  61. def create_entity():
  62. # Make sure no entity is selected
  63. general.clear_selection()
  64. # Create entity
  65. pyside_utils.trigger_context_menu_entry(
  66. entity_outliner, "Create entity", index=get_root_prefab_container_index()
  67. )
  68. # Wait a tick after entity creation to let events process
  69. PrefabWaiter.wait_for_propagation()
  70. # Moves an entity (wrapped by move_entity_before and move_entity_after)
  71. def _move_entity(source_name, target_name, move_after=False):
  72. source_index = index_for_name(source_name)
  73. target_index = index_for_name(target_name)
  74. if source_index is None:
  75. print(f"Failed to retrieve index for {source_name}")
  76. return
  77. if target_index is None:
  78. print(f"Failed to retrieve index for {target_name}")
  79. return
  80. target_row = target_index.row()
  81. if move_after:
  82. target_row += 1
  83. # Generate MIME data and directly inject it into the model instead of
  84. # generating mouse click operations, as it's more reliable and we're
  85. # testing the underlying drag & drop logic as opposed to Qt's mouse
  86. # handling here
  87. mime_data = entity_outliner_model.mimeData([source_index])
  88. entity_outliner_model.dropMimeData(
  89. mime_data, QtCore.Qt.MoveAction, target_row, 0, target_index.parent()
  90. )
  91. # Wait after move to let events (i.e. prefab propagation) process
  92. PrefabWaiter.wait_for_propagation()
  93. # Move an entity before another entity in the order by dragging the source above the target
  94. move_entity_before = lambda source_name, target_name: _move_entity(
  95. source_name, target_name, move_after=False
  96. )
  97. # Move an entity after another entity in the order by dragging the source below the target
  98. move_entity_after = lambda source_name, target_name: _move_entity(
  99. source_name, target_name, move_after=True
  100. )
  101. expected_order = []
  102. # 1) Open the empty Prefab Base level
  103. hydra.open_base_level()
  104. # 2) Add 5 entities to the outliner
  105. ENTITIES_TO_ADD = 5
  106. for i in range(ENTITIES_TO_ADD):
  107. create_entity()
  108. # Our new entity should be given a name with a number automatically
  109. new_entity = f"Entity{i+1}"
  110. # The new entity should be added to the bottom of its parent entity
  111. expected_order = expected_order + [new_entity]
  112. verify_entities_sorted(expected_order)
  113. # 3) Move "Entity5" to the top of the order
  114. move_entity_before("Entity5", "Entity1")
  115. expected_order = ["Entity5", "Entity1", "Entity2", "Entity3", "Entity4"]
  116. verify_entities_sorted(expected_order)
  117. # 4) Move "Entity2" to the bottom of the order
  118. move_entity_after("Entity2", "Entity4")
  119. expected_order = ["Entity5", "Entity1", "Entity3", "Entity4", "Entity2"]
  120. verify_entities_sorted(expected_order)
  121. # 5) Add another new entity, ensure the rest of the order is unchanged
  122. create_entity()
  123. expected_order = ["Entity5", "Entity1", "Entity3", "Entity4", "Entity2", "Entity6"]
  124. verify_entities_sorted(expected_order)
  125. if __name__ == "__main__":
  126. from editor_python_test_tools.utils import Report
  127. Report.start_test(EntityOutliner_EntityOrdering)