--- /dev/null
+SET(PKG_NAME "dali-usd-loader-dynamic-lib-func-override")
+
+SET(EXEC_NAME "tct-${PKG_NAME}-core")
+SET(RPM_NAME "core-${PKG_NAME}-tests")
+
+SET(CAPI_LIB "dali-usd-loader-dynamic-lib-func-override")
+
+# List of test case sources (Only these get parsed for test cases)
+SET(TC_SOURCES
+ utc-Dali-UsdLoaderDynamicLibFuncOverride.cpp
+ )
+
+# List of test harness files (Won't get parsed for test cases)
+SET(TEST_HARNESS_DIR "../dali-toolkit/dali-toolkit-test-utils")
+
+SET(TEST_HARNESS_SOURCES
+ ${TEST_HARNESS_DIR}/toolkit-adaptor.cpp
+ ${TEST_HARNESS_DIR}/toolkit-application.cpp
+ ${TEST_HARNESS_DIR}/toolkit-async-task-manager.cpp
+ ${TEST_HARNESS_DIR}/toolkit-event-thread-callback.cpp
+ ${TEST_HARNESS_DIR}/toolkit-environment-variable.cpp
+ ${TEST_HARNESS_DIR}/toolkit-input-method-context.cpp
+ ${TEST_HARNESS_DIR}/toolkit-input-method-options.cpp
+ ${TEST_HARNESS_DIR}/toolkit-lifecycle-controller.cpp
+ ${TEST_HARNESS_DIR}/toolkit-orientation.cpp
+ ${TEST_HARNESS_DIR}/toolkit-style-monitor.cpp
+ ${TEST_HARNESS_DIR}/toolkit-test-application.cpp
+ ${TEST_HARNESS_DIR}/toolkit-timer.cpp
+ ${TEST_HARNESS_DIR}/toolkit-trigger-event-factory.cpp
+ ${TEST_HARNESS_DIR}/toolkit-window.cpp
+ ${TEST_HARNESS_DIR}/toolkit-scene-holder.cpp
+ ${TEST_HARNESS_DIR}/dali-test-suite-utils.cpp
+ ${TEST_HARNESS_DIR}/dali-toolkit-test-suite-utils.cpp
+ ${TEST_HARNESS_DIR}/dummy-control.cpp
+ ${TEST_HARNESS_DIR}/mesh-builder.cpp
+ ${TEST_HARNESS_DIR}/test-actor-utils.cpp
+ ${TEST_HARNESS_DIR}/test-animation-data.cpp
+ ${TEST_HARNESS_DIR}/test-application.cpp
+ ${TEST_HARNESS_DIR}/test-button.cpp
+ ${TEST_HARNESS_DIR}/test-harness.cpp
+ ${TEST_HARNESS_DIR}/test-gesture-generator.cpp
+ ${TEST_HARNESS_DIR}/test-gl-abstraction.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-sync-impl.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-sync-object.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-buffer.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-command-buffer.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-framebuffer.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-controller.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-texture.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-sampler.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-program.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-pipeline.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-shader.cpp
+ ${TEST_HARNESS_DIR}/test-graphics-reflection.cpp
+ ${TEST_HARNESS_DIR}/test-platform-abstraction.cpp
+ ${TEST_HARNESS_DIR}/test-render-controller.cpp
+ ${TEST_HARNESS_DIR}/test-render-surface.cpp
+ ${TEST_HARNESS_DIR}/test-trace-call-stack.cpp
+)
+
+PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED
+ dali2-core
+ dali2-adaptor
+ dali2-toolkit
+ dali2-scene3d
+)
+
+ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror -DDEBUG_ENABLED)
+ADD_COMPILE_OPTIONS( ${${CAPI_LIB}_CFLAGS_OTHER} )
+
+ADD_DEFINITIONS(-DTEST_RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../resources\" )
+
+FOREACH(directory ${${CAPI_LIB}_LIBRARY_DIRS})
+ SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -L${directory}")
+ENDFOREACH(directory ${CAPI_LIB_LIBRARY_DIRS})
+
+INCLUDE_DIRECTORIES(
+ ../../../
+ ${${CAPI_LIB}_INCLUDE_DIRS}
+ ../dali-toolkit/dali-toolkit-test-utils
+)
+
+ADD_CUSTOM_COMMAND(
+ COMMAND ${SCRIPT_DIR}/tcheadgen.sh ${EXEC_NAME}.h ${TC_SOURCES}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ OUTPUT ${EXEC_NAME}.h
+ COMMENT "Generating test tables"
+ )
+
+ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.h ${EXEC_NAME}.cpp ${TC_SOURCES} ${TEST_HARNESS_SOURCES})
+TARGET_LINK_LIBRARIES(${EXEC_NAME}
+ ${${CAPI_LIB}_LIBRARIES}
+ -lpthread -ldl --coverage
+)
+
+INSTALL(PROGRAMS ${EXEC_NAME}
+ DESTINATION ${BIN_DIR}/${EXEC_NAME}
+)
--- /dev/null
+#include <test-harness.h>
+
+// Must come second
+#include "tct-dali-usd-loader-dynamic-lib-func-override-core.h"
+
+int main(int argc, char* const argv[])
+{
+ return TestHarness::RunTests(argc, argv, tc_array);
+}
--- /dev/null
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include <dali-scene3d/public-api/loader/load-result.h>
+#include <dali-scene3d/public-api/loader/model-loader.h>
+#include <dali-scene3d/public-api/loader/resource-bundle.h>
+#include <dali-scene3d/public-api/loader/scene-definition.h>
+#include <dali-test-suite-utils.h>
+#include <dlfcn.h>
+#include <string_view>
+
+using namespace Dali;
+using namespace Dali::Scene3D::Loader;
+
+namespace
+{
+struct Context
+{
+ ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {
+ return TEST_RESOURCE_DIR "/";
+ };
+
+ ResourceBundle resources;
+ SceneDefinition scene;
+ SceneMetadata metaData;
+
+ std::vector<AnimationDefinition> animations;
+ std::vector<AnimationGroupDefinition> animationGroups;
+ std::vector<CameraParameters> cameras;
+ std::vector<LightParameters> lights;
+
+ LoadResult loadResult{
+ resources,
+ scene,
+ metaData,
+ animations,
+ animationGroups,
+ cameras,
+ lights};
+
+ Dali::Scene3D::Loader::ModelLoader* loader;
+};
+
+bool gDlopenOverrideEnabled = false;
+bool gDlsymOverrideEnabled = false;
+
+extern "C" void* DlopenProxy(const char* filename, int flag)
+{
+ if(gDlopenOverrideEnabled)
+ {
+ return nullptr;
+ }
+ else
+ {
+ return dlopen("libdali2-scene3d.so", RTLD_LAZY);
+ }
+}
+
+extern "C" void* DlsymProxy(void* handle, const char* symbol)
+{
+ if(gDlsymOverrideEnabled)
+ {
+ return nullptr;
+ }
+ else
+ {
+ return dlsym(handle, symbol);
+ }
+}
+
+} // namespace
+
+int UtcDaliUsdLoaderDlopenFail(void)
+{
+ // Only make dlopen fail
+ gDlopenOverrideEnabled = true;
+ gDlsymOverrideEnabled = false;
+
+ Context ctx;
+
+ ctx.loader = new Dali::Scene3D::Loader::ModelLoader(TEST_RESOURCE_DIR "/usd/CesiumMan.usdz", ctx.pathProvider(ResourceType::Mesh) + "/", ctx.loadResult);
+ DALI_TEST_EQUAL(ctx.loader->LoadModel(ctx.pathProvider, true), false);
+
+ DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
+ DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
+
+ DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size());
+ DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size());
+ DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size());
+ DALI_TEST_EQUAL(0, ctx.resources.mShaders.size());
+ DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size());
+
+ DALI_TEST_EQUAL(0, ctx.cameras.size());
+ DALI_TEST_EQUAL(0, ctx.lights.size());
+ DALI_TEST_EQUAL(0, ctx.animations.size());
+ DALI_TEST_EQUAL(0, ctx.animationGroups.size());
+
+ delete ctx.loader;
+
+ END_TEST;
+}
+
+int UtcDaliUsdLoaderDlsymFail(void)
+{
+ // Only make dlsym fail
+ gDlopenOverrideEnabled = false;
+ gDlsymOverrideEnabled = true;
+
+ Context ctx;
+
+ ctx.loader = new Dali::Scene3D::Loader::ModelLoader(TEST_RESOURCE_DIR "/usd/CesiumMan.usdz", ctx.pathProvider(ResourceType::Mesh) + "/", ctx.loadResult);
+ DALI_TEST_EQUAL(ctx.loader->LoadModel(ctx.pathProvider, true), false);
+
+ DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
+ DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
+
+ DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size());
+ DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size());
+ DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size());
+ DALI_TEST_EQUAL(0, ctx.resources.mShaders.size());
+ DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size());
+
+ DALI_TEST_EQUAL(0, ctx.cameras.size());
+ DALI_TEST_EQUAL(0, ctx.lights.size());
+ DALI_TEST_EQUAL(0, ctx.animations.size());
+ DALI_TEST_EQUAL(0, ctx.animationGroups.size());
+
+ delete ctx.loader;
+
+ END_TEST;
+}
{
namespace
{
-static constexpr std::string_view OBJ_EXTENSION = ".obj";
-static constexpr std::string_view GLTF_EXTENSION = ".gltf";
-static constexpr std::string_view GLB_EXTENSION = ".glb";
-static constexpr std::string_view DLI_EXTENSION = ".dli";
-static constexpr std::string_view USD_EXTENSION = ".usd";
-static constexpr std::string_view USDZ_EXTENSION = ".usdz";
-static constexpr std::string_view USDA_EXTENSION = ".usda";
-static constexpr std::string_view USDC_EXTENSION = ".usdc";
-static constexpr std::string_view METADATA_EXTENSION = "metadata";
+constexpr std::string_view OBJ_EXTENSION = ".obj";
+constexpr std::string_view GLTF_EXTENSION = ".gltf";
+constexpr std::string_view GLB_EXTENSION = ".glb";
+constexpr std::string_view DLI_EXTENSION = ".dli";
+constexpr std::string_view USD_EXTENSION = ".usd";
+constexpr std::string_view USDZ_EXTENSION = ".usdz";
+constexpr std::string_view USDA_EXTENSION = ".usda";
+constexpr std::string_view USDC_EXTENSION = ".usdc";
+constexpr std::string_view METADATA_EXTENSION = "metadata";
const char* USD_LOADER_SO("libdali2-usd-loader.so");
const char* CREATE_USD_LOADER_SYMBOL("CreateUsdLoader");
+// Custom deleter for dlopen handles
+void DlcloseDeleter(void* handle)
+{
+ if(handle)
+ {
+ dlclose(handle);
+ }
+}
+
+// Static shared pointer to store a pointer to the dlopen handle
+std::shared_ptr<void*> gUsdLoaderHandle(nullptr, DlcloseDeleter);
+
+using CreateUsdLoaderFunc = ModelLoaderImpl* (*)();
+CreateUsdLoaderFunc gCreateUsdLoaderFunc(nullptr);
+
+// Poxy function for `dlopen` to allow easy overriding in test environments.
+extern "C" void* DlopenProxy(const char* filename, int flag)
+{
+ // Calls the real dlopen
+ return dlopen(filename, flag);
+}
+
+// Poxy function for `dlsym` to allow easy overriding in test environments.
+extern "C" void* DlsymProxy(void* handle, const char* name)
+{
+ // Calls the real dlsym
+ return dlsym(handle, name);
+}
+
} // namespace
ModelLoader::ModelLoader(const std::string& modelUrl, const std::string& resourceDirectoryUrl, Dali::Scene3D::Loader::LoadResult& loadResult)
// Attempt to load the USD loader library dynamically
// Once loaded we will keep it open so that any subsequent loading of USD models
// doesn't require loading the same library repeatedly.
- void* handle(dlopen(USD_LOADER_SO, RTLD_LAZY));
- if(!handle)
+ if(!gUsdLoaderHandle || !(*gUsdLoaderHandle))
{
- // The shared library failed to load
- DALI_LOG_ERROR("ModelLoader::CreateModelLoader, dlopen error: %s\n", dlerror());
- return;
- }
-
- // Dynamically link to the CreateUsdLoader function in the shared library at runtime.
- using CreateUsdLoaderFunc = Dali::Scene3D::Loader::ModelLoaderImpl* (*)();
- CreateUsdLoaderFunc createUsdLoader = reinterpret_cast<CreateUsdLoaderFunc>(dlsym(handle, CREATE_USD_LOADER_SYMBOL));
-
- if(!createUsdLoader)
- {
- // Close the shared library if the symbol couldn't be found
- dlclose(handle);
- DALI_LOG_ERROR("Cannot find CreateUsdLoader function: %s\n", dlerror());
-
- return;
+ void* handle = DlopenProxy(USD_LOADER_SO, RTLD_LAZY);
+
+ if(!handle)
+ {
+ // The shared library failed to load
+ DALI_LOG_ERROR("ModelLoader::CreateModelLoader, dlopen error: %s\n", dlerror());
+ return;
+ }
+
+ // Store the handle in the shared_ptr (passing the handle and the custom deleter)
+ gUsdLoaderHandle = std::shared_ptr<void*>(new void*(handle), [](void* ptr) {
+ if(ptr)
+ {
+ DlcloseDeleter(*(static_cast<void**>(ptr))); // Call custom deleter
+ delete static_cast<void**>(ptr); // Clean up dynamically allocated memory
+ }
+ });
+
+ // Dynamically link to the CreateUsdLoader function in the shared library at runtime.
+ // Cache the function pointer only if it hasn't been loaded yet
+ if(!gCreateUsdLoaderFunc)
+ {
+ gCreateUsdLoaderFunc = reinterpret_cast<CreateUsdLoaderFunc>(DlsymProxy(*gUsdLoaderHandle.get(), CREATE_USD_LOADER_SYMBOL));
+ if(!gCreateUsdLoaderFunc)
+ {
+ // If the symbol couldn't be found, reset the shared_ptr to invoke the custom deleter
+ gUsdLoaderHandle.reset(); // This will automatically call dlclose via the custom deleter
+
+ DALI_LOG_ERROR("Cannot find CreateUsdLoader function: %s\n", dlerror());
+
+ return;
+ }
+ }
}
// Create an instance of USD loader
- mImpl = ModelLoaderImplUniquePtr(createUsdLoader());
+ mImpl = ModelLoaderImplUniquePtr(gCreateUsdLoaderFunc());
}
else
{
GetResources().LoadResources(pathProvider);
}
}
-
} // namespace Dali::Scene3D::Loader