EntityOutliner_EntityOrdering.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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 editor_python_test_tools.hydra_editor_utils as hydra
  27. import pyside_utils
  28. from editor_python_test_tools.utils import Report
  29. from editor_python_test_tools.wait_utils import PrefabWaiter
  30. # Grab the Editor, Entity Outliner, and Outliner Model
  31. editor_window = pyside_utils.get_editor_main_window()
  32. entity_outliner = pyside_utils.find_child_by_hierarchy(
  33. editor_window, ..., "EntityOutlinerWidgetUI", ..., "m_objectTree"
  34. )
  35. entity_outliner_model = entity_outliner.model()
  36. # Get the outliner index for the root prefab container entity
  37. def get_root_prefab_container_index():
  38. return entity_outliner_model.index(0, 0)
  39. # Get the outliner index for the top level entity of a given name
  40. def index_for_name(name):
  41. root_index = get_root_prefab_container_index()
  42. for row in range(entity_outliner_model.rowCount(root_index)):
  43. row_index = entity_outliner_model.index(row, 0, root_index)
  44. if row_index.data() == name:
  45. return row_index
  46. return None
  47. # Validate that the outliner top level entity order matches the expected order
  48. def verify_entities_sorted(expected_order):
  49. actual_order = []
  50. root_index = get_root_prefab_container_index()
  51. for row in range(entity_outliner_model.rowCount(root_index)):
  52. row_index = entity_outliner_model.index(row, 0, root_index)
  53. actual_order.append(row_index.data())
  54. sorted_correctly = actual_order == expected_order
  55. Report.result(Tests.entities_sorted, sorted_correctly)
  56. if not sorted_correctly:
  57. print(f"Expected entity order: {expected_order}")
  58. print(f"Actual entity order: {actual_order}")
  59. # Creates an entity from the outliner context menu
  60. def create_entity():
  61. # Create entity
  62. pyside_utils.trigger_context_menu_entry(
  63. entity_outliner, "Create entity", index=get_root_prefab_container_index()
  64. )
  65. # Wait a tick after entity creation to let events process
  66. PrefabWaiter.wait_for_propagation()
  67. # Moves an entity (wrapped by move_entity_before and move_entity_after)
  68. def _move_entity(source_name, target_name, move_after=False):
  69. source_index = index_for_name(source_name)
  70. target_index = index_for_name(target_name)
  71. if source_index is None:
  72. print(f"Failed to retrieve index for {source_name}")
  73. return
  74. if target_index is None:
  75. print(f"Failed to retrieve index for {target_name}")
  76. return
  77. target_row = target_index.row()
  78. if move_after:
  79. target_row += 1
  80. # Generate MIME data and directly inject it into the model instead of
  81. # generating mouse click operations, as it's more reliable and we're
  82. # testing the underlying drag & drop logic as opposed to Qt's mouse
  83. # handling here
  84. mime_data = entity_outliner_model.mimeData([source_index])
  85. entity_outliner_model.dropMimeData(
  86. mime_data, QtCore.Qt.MoveAction, target_row, 0, target_index.parent()
  87. )
  88. # Wait after move to let events (i.e. prefab propagation) process
  89. PrefabWaiter.wait_for_propagation()
  90. # Move an entity before another entity in the order by dragging the source above the target
  91. move_entity_before = lambda source_name, target_name: _move_entity(
  92. source_name, target_name, move_after=False
  93. )
  94. # Move an entity after another entity in the order by dragging the source below the target
  95. move_entity_after = lambda source_name, target_name: _move_entity(
  96. source_name, target_name, move_after=True
  97. )
  98. expected_order = []
  99. # 1) Open the empty Prefab Base level
  100. hydra.open_base_level()
  101. # 2) Add 5 entities to the outliner
  102. ENTITIES_TO_ADD = 5
  103. for i in range(ENTITIES_TO_ADD):
  104. create_entity()
  105. # Our new entity should be given a name with a number automatically
  106. new_entity = f"Entity{i+1}"
  107. # The new entity should be added to the bottom of its parent entity
  108. expected_order = expected_order + [new_entity]
  109. verify_entities_sorted(expected_order)
  110. # 3) Move "Entity5" to the top of the order
  111. move_entity_before("Entity5", "Entity1")
  112. expected_order = ["Entity5", "Entity1", "Entity2", "Entity3", "Entity4"]
  113. verify_entities_sorted(expected_order)
  114. # 4) Move "Entity2" to the bottom of the order
  115. move_entity_after("Entity2", "Entity4")
  116. expected_order = ["Entity5", "Entity1", "Entity3", "Entity4", "Entity2"]
  117. verify_entities_sorted(expected_order)
  118. # 5) Add another new entity, ensure the rest of the order is unchanged
  119. create_entity()
  120. expected_order = ["Entity5", "Entity1", "Entity3", "Entity4", "Entity2", "Entity6"]
  121. verify_entities_sorted(expected_order)
  122. if __name__ == "__main__":
  123. from editor_python_test_tools.utils import Report
  124. Report.start_test(EntityOutliner_EntityOrdering)