123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- """
- Copyright (c) Contributors to the Open 3D Engine Project.
- For complete copyright and license terms please see the LICENSE at the root of this distribution.
- SPDX-License-Identifier: Apache-2.0 OR MIT
- """
- # Import builtin libraries
- import pytest
- import logging
- import os
- import json
- # Import fixtures
- from ..ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
- from ..ap_fixtures.ap_setup_fixture import ap_setup_fixture as ap_setup_fixture
- # Import LyShared
- import ly_test_tools.o3de.pipeline_utils as utils
- # Use the following logging pattern to hook all test logging together:
- logger = logging.getLogger(__name__)
- # Configuring the logging is done in ly_test_tools at the following location:
- # ~/dev/Tools/LyTestTools/ly_test_tools/log/py_logging_util.py
- @pytest.fixture
- def local_resources(request, workspace, ap_setup_fixture):
- ap_setup_fixture["tests_dir"] = os.path.dirname(os.path.realpath(__file__))
- @pytest.mark.usefixtures("asset_processor")
- @pytest.mark.usefixtures("ap_setup_fixture")
- @pytest.mark.usefixtures("local_resources")
- @pytest.mark.parametrize("project", ["AutomatedTesting"])
- @pytest.mark.SUITE_main
- class TestsPythonAssetProcessing_APBatch(object):
- @property
- def asset_processor_extra_params(self):
- return [
- # Disabling Atom assets disables most products, using the debugOutput flag ensures one product is output.
- "--debugOutput",
- # By default, if job priorities are equal, jobs run in an arbitrary order. This makes sure
- # jobs are run by sorting on the database source name, so they run in the same order each time
- # when this test is run.
- "--sortJobsByDBSourceName",
- # Disabling Atom products means this asset won't need a lot of source dependencies to be processed,
- # keeping the scope of this test down.
- "--regset=\"/O3DE/SceneAPI/AssetImporter/SkipAtomOutput=true\"",
- # The bug this regression test happened when the same builder processed FBX files with and without Python.
- # This flag ensures that only one builder is launched, so that situation can be replicated.
- "--regset=\"/Amazon/AssetProcessor/Settings/Jobs/maxJobs=1\""]
- def test_ProcessAssetWithoutScriptAfterAssetWithScript_ScriptOnlyRunsOnExpectedAsset(self, workspace, ap_setup_fixture, asset_processor):
- # This is a regression test. The situation it's testing is, the Python script to run
- # defined in scene manifest files was persisting in a single builder. So if
- # that builder processed file a.fbx, then b.fbx, and a.fbx has a Python script to run,
- # it was also running that Python script on b.fbx.
- asset_processor.prepare_test_environment(ap_setup_fixture["tests_dir"], "TwoSceneFiles_OneWithPythonOneWithout_PythonOnlyRunsOnFirstScene")
- result, _ = asset_processor.batch_process(extra_params=self.asset_processor_extra_params)
- assert result, "AP Batch failed"
- expected_product_list = [
- "a_simple_box_with_script.fbx.dbgsg",
- "b_simple_box_no_script.fbx.dbgsg"
- ]
- missing_assets, _ = utils.compare_assets_with_cache(expected_product_list,
- asset_processor.project_test_cache_folder())
- assert not missing_assets, f'The following assets were expected to be in, but not found in cache: {str(missing_assets)}'
- # The Python script loaded in the scene manifest will write a log file with the source file's name
- # to the temp folder. This is the easiest way to have the internal Python there communicate with this test.
- expected_path = os.path.join(asset_processor.project_test_source_folder(), "a_simple_box_with_script_fbx.log")
- unexpected_path = os.path.join(asset_processor.project_test_source_folder(), "b_simple_box_no_script_fbx.log")
-
- # Simple check to make sure the Python script in the scene manifest ran on the file it should have ran on.
- assert os.path.exists(expected_path), f"Did not find expected output test asset {expected_path}"
- # If this test fails here, it means the Python script from the first processed FBX file is being run
- # on the second FBX file, when it should not be.
- assert not os.path.exists(unexpected_path), f"Found unexpected output test asset {unexpected_path}"
- def find_user_defined_property(self, filename: str, text: str):
- # find the user defined property pattern in a file
- with open(filename) as f:
- content = f.readlines()
- for line in content:
- if line.rstrip().endswith(text):
- return True
- return False
- def compute_udp_asset_debug_file(self, workspace, asset_processor, debug_filename, ap_setup_fixture):
- asset_folder_name = 'UserDefinedProperties'
- return self.compute_asset_debug_file(workspace, asset_processor, debug_filename, asset_folder_name, ap_setup_fixture)
- def compute_asset_debug_file(self, workspace, asset_processor, debug_filename, folder_name, ap_setup_fixture):
- # computes the file name of the debug file for a UserDefinedProperties test file
- asset_processor.prepare_test_environment(ap_setup_fixture["tests_dir"], folder_name)
- result, _ = asset_processor.batch_process(extra_params=self.asset_processor_extra_params)
- assert result, "AP Batch failed"
- # compute the cache path
- cache_folder = asset_processor.project_test_cache_folder()
- # compute the file name to the debug file
- asset_debug_filename = os.path.join(cache_folder, debug_filename)
- if os.path.isfile(asset_debug_filename) == False:
- raise Exception(f"Missing file {asset_debug_filename}")
- return asset_debug_filename
- def test_ProcessSceneWithMetadata_SupportedMayaDataTypes_Work(self, workspace, ap_setup_fixture, asset_processor):
- # This test loads the debug output file for an FBX exported by Maya that has a few user defined properties
- asset_dbgsg = 'maya_with_attributes.fbx.dbgsg'
- dbgsg_file = self.compute_udp_asset_debug_file(workspace, asset_processor, asset_dbgsg, ap_setup_fixture)
- assert self.find_user_defined_property(dbgsg_file, 'o3de_atom_lod: false'), "Malformed o3de_atom_lod value"
- assert self.find_user_defined_property(dbgsg_file, 'o3de_atom_material: 0'), "Malformed o3de_atom_material value"
- assert self.find_user_defined_property(dbgsg_file, 'o3de_default_lod: 0.000000'), "Malformed o3de_default_lod value"
- assert self.find_user_defined_property(dbgsg_file, 'o3de_default_material: gem/sponza/assets/objects/sponza_mat_bricks.azmaterial'), "Malformed o3de_default_material value"
- def test_ProcessSceneWithMetadata_SupportedMaxDataTypes_Work(self, workspace, ap_setup_fixture, asset_processor):
- # This test loads the debug output file for an FBX exported by Max that has a few user defined properties
- asset_dbgsg = 'max_with_attributes.fbx.dbgsg'
- dbgsg_file = self.compute_udp_asset_debug_file(workspace, asset_processor, asset_dbgsg, ap_setup_fixture)
- assert self.find_user_defined_property(dbgsg_file, 'o3de_atom_material: 0'), "Malformed o3de_atom_material value"
- assert self.find_user_defined_property(dbgsg_file, 'o3de_phyx_lodY: 0.000000'), "Malformed o3de_phyx_lodY value"
- assert self.find_user_defined_property(dbgsg_file, 'o3de_default_lod: 0.000000'), "Malformed o3de_default_lod value"
- assert self.find_user_defined_property(dbgsg_file, 'o3de_atom_lod: false'), "Malformed o3de_atom_lod value"
- assert self.find_user_defined_property(dbgsg_file, 'o3de_default_material: gem/sponza/assets/objects/sponza_mat_bricks.azmaterial'), "Malformed o3de_default_material value"
- def test_ProcessSceneWithMetadata_SupportedBlenderDataTypes_Work(self, workspace, ap_setup_fixture, asset_processor):
- # This test loads the debug output file for an FBX exported by Blender that has a few user defined properties
- asset_dbgsg = 'blender_with_attributes.fbx.dbgsg'
- dbgsg_file = self.compute_udp_asset_debug_file(workspace, asset_processor, asset_dbgsg, ap_setup_fixture)
- assert self.find_user_defined_property(dbgsg_file, 'o3de_atom_material: 0'), "Malformed o3de_atom_material value"
- assert self.find_user_defined_property(dbgsg_file, 'o3de_default_lod: 0.000000'), "Malformed o3de_default_lod value"
- assert self.find_user_defined_property(dbgsg_file, 'o3de_default_material: gem/sponza/assets/objects/sponza_mat_bricks.azmaterial'), "Malformed o3de_default_material value"
- assert self.find_user_defined_property(dbgsg_file, 'o3de_atom_lod: 0'), "Malformed o3de_atom_lod value"
- def test_ProcessSceneWithMetadata_DebugSceneManifest_Work(self, workspace, ap_setup_fixture, asset_processor):
- # This test detects the debug .assetinfo file in the cahce folder
- assetinfo_dbg = 'blender_with_attributes.fbx.assetinfo.dbg'
- assetinfo_dbg_file = self.compute_udp_asset_debug_file(workspace, asset_processor, assetinfo_dbg, ap_setup_fixture)
- assert assetinfo_dbg_file, "The debug assetinfo file is missing"
- def test_ProcessSceneWithCommonParent_DefaultMeshGroups_Work(self, workspace, ap_setup_fixture, asset_processor):
- # This test validates that the correct meshes are included and excluded in the default mesh groups
- # of a scene containing mesh nodes that have a common parent transform node
- assetinfo_dbg_list = {
- "blender_with_common_parent.fbx.assetinfo.dbg",
- "maya_with_common_parent.fbx.assetinfo.dbg"
- }
- expected_mesh_nodes = {
- "RootNode.AstroVAC v10.CPU:1.CPU1.CUPCase",
- "RootNode.AstroVAC v10.CPU:1.CPU1.CPUUserDoor",
- "RootNode.AstroVAC v10.CPU:1.CPU1.CPUCover"
- }
- for assetinfo_dbg in assetinfo_dbg_list:
- asset_folder_name = 'MeshGroups'
- assetinfo_dbg_file = self.compute_asset_debug_file(workspace, asset_processor, assetinfo_dbg, asset_folder_name, ap_setup_fixture)
- assert assetinfo_dbg_file, "The debug assetinfo file is missing"
- with open(assetinfo_dbg_file, 'r') as file :
- filedata = file.read()
- dgb_dict = json.loads(filedata)
- found_mesh_nodes = set()
- # Check that each default mesh group contains one of the expected mesh nodes in its selected
- # node list and has the rest of the expected mesh nodes in its unselected node list
- groups = dgb_dict['values']
- for group in groups:
- if 'MeshGroup' in group.get('$type', str()) and group.get('name', str()).startswith('default_'):
- selection_lists = group.get('nodeSelectionList')
- selected_node_list = selection_lists.get('selectedNodes', [])
- assert len(selected_node_list) == 1, "Selected node list does not contain exactly one mesh"
- current_set = expected_mesh_nodes.copy()
- current_set.remove(selected_node_list[0])
- unselected_node_list = selection_lists.get('unselectedNodes', [])
- assert current_set == set(unselected_node_list), "Unselected node list does not contain the correct meshes"
- found_mesh_nodes.add(selected_node_list[0])
- assert found_mesh_nodes == expected_mesh_nodes, "Not all expected mesh groups found"
- def test_ProcessAssetScript_SimpleFbx_HasExpectedProducts(self, workspace, ap_setup_fixture, asset_processor):
- # This tests all of the entry points a scene builder can script:
- # - OnUpdateManifest() returns a JSON string to use as the scene manifest; this must succeed to continue the scene pipe
- # - OnPrepareForExport() returns an export product list; this test should produce a simple.fbx.fake_asset export product
- asset_processor.prepare_test_environment(ap_setup_fixture["tests_dir"], "script_basics")
- result, _ = asset_processor.batch_process(extra_params=self.asset_processor_extra_params)
- assert result, "AP Batch failed"
- expected_product_list = [
- "simple.fbx.fake_asset"
- ]
- missing_assets, _ = utils.compare_assets_with_cache(expected_product_list, asset_processor.project_test_cache_folder())
- assert not missing_assets, f'The following assets were expected to be in, but not found in cache: {str(missing_assets)}'
|