Add macOS bundles support to test framework
authorCharles Giessen <charles@lunarg.com>
Tue, 6 Dec 2022 20:45:15 +0000 (13:45 -0700)
committerCharles Giessen <46324611+charles-lunarg@users.noreply.github.com>
Mon, 9 Jan 2023 23:25:53 +0000 (16:25 -0700)
Allow basic tests to be written which exercise the bundle discovery and loading logic
in the loader. This allows testing whether drivers found in bundles disables searching
for drivers on the rest of the system.

This commit also inclues fixes for a half dozen non essential spelling mistakes.

loader/loader.c
loader/trampoline.c
tests/framework/shim/CMakeLists.txt
tests/framework/shim/shim.h
tests/framework/shim/shim_common.cpp
tests/framework/shim/unix_shim.cpp
tests/framework/test_environment.cpp
tests/framework/test_environment.h
tests/framework/test_util.h
tests/loader_regression_tests.cpp

index c1af4a31ba65c9259f57d4f613c85b30f08a79cf..fff5ca9b85c95d84e28544ca5944b28b9e230886 100644 (file)
@@ -127,7 +127,7 @@ loader_api_version loader_combine_version(uint32_t major, uint32_t minor, uint32
 bool loader_check_version_meets_required(loader_api_version required, loader_api_version version) {
     // major version is satisfied
     return (version.major > required.major) ||
-           // major version is equal, minor version is patch version is gerater to minimum minor
+           // major version is equal, minor version is patch version is greater to minimum minor
            (version.major == required.major && version.minor > required.minor) ||
            // major and minor version are equal, patch version is greater or equal to minimum patch
            (version.major == required.major && version.minor == required.minor && version.patch >= required.patch);
index 9066a087804e4bbd76a55626a22fba221625e061..740898ff6e244c21d047787d889b43b7e24c1182 100644 (file)
@@ -38,7 +38,7 @@
 
 // Trampoline entrypoints are in this file for core Vulkan commands
 
-/* vkGetInstanceProcAddr: Get global level or instance level entrypoint addressess.
+/* vkGetInstanceProcAddr: Get global level or instance level entrypoint addresses.
  * @param instance
  * @param pName
  * @return
@@ -55,7 +55,7 @@
  *
  * Note:
  * Vulkan header updated 1.2.193 changed the behavior of vkGetInstanceProcAddr for global entrypoints. They used to always be
- * returned regardless of the value of the instance paramtere. The spec was amended in this version to only allow querying global
+ * returned regardless of the value of the instance parameter. The spec was amended in this version to only allow querying global
  * level entrypoints with a NULL instance. However, as to not break old applications, the new behavior is only applied if the
  * instance passed in is both valid and minor version is greater than 1.2, which was when this change in behavior occurred. Only
  * instances with a newer version will get the new behavior.
@@ -2131,7 +2131,7 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdCopyImage(VkCommandBuffer commandB
     const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer);
     if (NULL == disp) {
         loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,
-                   "vkCmdCopyImage: Invalid commandBuffer [VUID-vkCmdCopyImage-devcommandBufferice-parameter]");
+                   "vkCmdCopyImage: Invalid commandBuffer [VUID-vkCmdCopyImage-commandBuffer-parameter]");
         abort(); /* Intentionally fail so user can correct issue. */
     }
 
index 82c2573f52ca5a5de3e0dd45af3221f279f337a5..919dd36263871d4209c0c8f161fde94673e50c83 100644 (file)
@@ -31,6 +31,7 @@ if (WIN32)
 elseif(UNIX)
     if(APPLE)
         add_library(shim-library SHARED unix_shim.cpp)
+        target_link_libraries(shim-library PRIVATE "-framework CoreFoundation")
     else()
         add_library(shim-library STATIC unix_shim.cpp)
     endif()
index b619465e27daac01f97232851851969f374dad04..44d54dd149755190cf99927179f7d85825c3d7b0 100644 (file)
@@ -194,6 +194,10 @@ struct PlatformShim {
     bool use_fake_elevation = false;
 
     std::vector<DirEntry> dir_entries;
+
+    #if defined(__APPLE__)
+    std::string bundle_contents;
+    #endif
 #endif
 };
 
index 2c16a8a5b981e690a60bc79d53225f52012603b2..02f71b85df60f92c60f2967c9cd8299297e91dcf 100644 (file)
@@ -180,10 +180,15 @@ void PlatformShim::redirect_category(fs::path const& new_path, ManifestCategory
         paths.push_back((home / ".config").str());
         paths.push_back((home / ".local/share").str());
     }
-    parse_and_add_env_var_override(paths, get_env_var("XDG_CONFIG_DIRS"));
-    parse_and_add_env_var_override(paths, get_env_var("XDG_CONFIG_HOME"));
-    parse_and_add_env_var_override(paths, get_env_var("XDG_DATA_DIRS"));
-    parse_and_add_env_var_override(paths, get_env_var("XDG_DATA_HOME"));
+    // Don't report errors on apple - these env-vars are not suppose to be defined
+    bool report_errors = true;
+#if defined(__APPLE__)
+    report_errors = false;
+#endif
+    parse_and_add_env_var_override(paths, get_env_var("XDG_CONFIG_DIRS", report_errors));
+    parse_and_add_env_var_override(paths, get_env_var("XDG_CONFIG_HOME", report_errors));
+    parse_and_add_env_var_override(paths, get_env_var("XDG_DATA_DIRS", report_errors));
+    parse_and_add_env_var_override(paths, get_env_var("XDG_DATA_HOME", report_errors));
     if (category == ManifestCategory::explicit_layer) {
         parse_and_add_env_var_override(paths, get_env_var("VK_LAYER_PATH", false));  // don't report failure
     }
index e930293b89ad89d0c10662a37744a845d8e327e4..1f039420c9aa1fdc60aff032fa25b47f263e728b 100644 (file)
 
 #include "shim.h"
 
+#include <algorithm>
+
+#if defined(__APPLE__)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
 static PlatformShim platform_shim;
 extern "C" {
 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
@@ -270,6 +276,29 @@ FRAMEWORK_EXPORT char* __SECURE_GETENV_FUNC_NAME(const char* name) {
 }
 #endif
 
+#if defined(__APPLE__)
+FRAMEWORK_EXPORT CFBundleRef my_CFBundleGetMainBundle() {
+    static CFBundleRef global_bundle{};
+    return reinterpret_cast<CFBundleRef>(&global_bundle);
+}
+FRAMEWORK_EXPORT CFURLRef my_CFBundleCopyResourcesDirectoryURL(CFBundleRef bundle) {
+    static CFURLRef global_url{};
+    return reinterpret_cast<CFURLRef>(&global_url);
+}
+FRAMEWORK_EXPORT Boolean my_CFURLGetFileSystemRepresentation(CFURLRef url, Boolean resolveAgainstBase, UInt8* buffer,
+                                                             CFIndex maxBufLen) {
+    if (!platform_shim.bundle_contents.empty()) {
+        size_t copy_len = platform_shim.bundle_contents.size();
+        if (copy_len > maxBufLen) {
+            copy_len = maxBufLen;
+        }
+        strncpy(reinterpret_cast<char*>(buffer), platform_shim.bundle_contents.c_str(), copy_len);
+        return TRUE;
+    }
+    return FALSE;
+}
+#endif
+
 /* Shiming functions on apple is limited by the linker prefering to not use functions in the
  * executable in loaded dylibs. By adding an interposer, we redirect the linker to use our
  * version of the function over the real one, thus shimming the system function.
@@ -299,5 +328,12 @@ __attribute__((used)) static Interposer _interpose_secure_getenv MACOS_ATTRIB =
 __attribute__((used)) static Interposer _interpose__secure_getenv MACOS_ATTRIB = {VOIDP_CAST(my__secure_getenv),
                                                                                   VOIDP_CAST(__secure_getenv)};
 #endif
+__attribute__((used)) static Interposer _interpose_CFBundleGetMainBundle MACOS_ATTRIB = {VOIDP_CAST(my_CFBundleGetMainBundle),
+                                                                                         VOIDP_CAST(CFBundleGetMainBundle)};
+__attribute__((used)) static Interposer _interpose_CFBundleCopyResourcesDirectoryURL MACOS_ATTRIB = {
+    VOIDP_CAST(my_CFBundleCopyResourcesDirectoryURL), VOIDP_CAST(CFBundleCopyResourcesDirectoryURL)};
+__attribute__((used)) static Interposer _interpose_CFURLGetFileSystemRepresentation MACOS_ATTRIB = {
+    VOIDP_CAST(my_CFURLGetFileSystemRepresentation), VOIDP_CAST(CFURLGetFileSystemRepresentation)};
+
 #endif
 }  // extern "C"
index 308230e60e2c2b0f3625aed7227538c7ba8f9a25..079bbcd928f82b58887aec75167a61da03062f57 100644 (file)
@@ -360,6 +360,7 @@ FrameworkEnvironment::FrameworkEnvironment(bool enable_log, bool set_default_sea
     folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("implicit_layer_manifests"));
     folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("override_layer_manifests"));
     folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("app_package_manifests"));
+    folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("macos_bundle"));
 
     platform_shim->redirect_all_paths(get_folder(ManifestLocation::null).location());
     if (set_default_search_paths) {
@@ -367,6 +368,13 @@ FrameworkEnvironment::FrameworkEnvironment(bool enable_log, bool set_default_sea
         platform_shim->set_path(ManifestCategory::explicit_layer, get_folder(ManifestLocation::explicit_layer).location());
         platform_shim->set_path(ManifestCategory::implicit_layer, get_folder(ManifestLocation::implicit_layer).location());
     }
+#if defined(__APPLE__)
+    // Necessary since bundles look in sub folders for manifests, not the test framework folder itself
+    auto bundle_location = get_folder(ManifestLocation::macos_bundle).location();
+    platform_shim->redirect_path(bundle_location / "vulkan/icd.d", bundle_location);
+    platform_shim->redirect_path(bundle_location / "vulkan/explicit_layer.d", bundle_location);
+    platform_shim->redirect_path(bundle_location / "vulkan/implicit_layer.d", bundle_location);
+#endif
 }
 
 void FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept {
@@ -379,6 +387,9 @@ void FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept {
     if (icd_details.discovery_type == ManifestDiscoveryType::windows_app_package) {
         folder = &get_folder(ManifestLocation::windows_app_package);
     }
+    if (icd_details.discovery_type == ManifestDiscoveryType::macos_bundle) {
+        folder = &get_folder(ManifestLocation::macos_bundle);
+    }
     if (!icd_details.is_fake) {
         fs::path new_driver_name = fs::path(icd_details.icd_manifest.lib_path).stem() + "_" + std::to_string(cur_icd_index) +
                                    fs::path(icd_details.icd_manifest.lib_path).extension();
@@ -415,6 +426,8 @@ void FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept {
             add_env_var_vk_icd_filenames += (folder->location() / full_json_name).str();
             set_env_var("VK_ADD_DRIVER_FILES", add_env_var_vk_icd_filenames);
             break;
+        case (ManifestDiscoveryType::macos_bundle):
+            platform_shim->add_manifest(ManifestCategory::icd, icds.back().manifest_path);
         case (ManifestDiscoveryType::none):
             break;
 #ifdef _WIN32
@@ -456,7 +469,7 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife
             if (!env_var_vk_layer_paths.empty()) {
                 env_var_vk_layer_paths += OS_ENV_VAR_LIST_SEPARATOR;
             }
-            if(layer_details.is_dir) {
+            if (layer_details.is_dir) {
                 env_var_vk_layer_paths += fs_ptr->location().str();
             } else {
                 env_var_vk_layer_paths += fs_ptr->location().str() + OS_ENV_VAR_LIST_SEPARATOR + layer_details.json_name;
@@ -475,6 +488,9 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife
         case (ManifestDiscoveryType::override_folder):
             fs_ptr = &get_folder(ManifestLocation::override_layer);
             break;
+        case (ManifestDiscoveryType::macos_bundle):
+            fs_ptr = &(get_folder(ManifestLocation::macos_bundle));
+            break;
         case (ManifestDiscoveryType::none):
             break;
     }
@@ -520,6 +536,11 @@ fs::FolderManager& FrameworkEnvironment::get_folder(ManifestLocation location) n
     // index it directly using the enum location since they will always be in that order
     return folders.at(static_cast<size_t>(location));
 }
+#if defined(__APPLE__)
+void FrameworkEnvironment::setup_macos_bundle() noexcept {
+    platform_shim->bundle_contents = get_folder(ManifestLocation::macos_bundle).location().str();
+}
+#endif
 const char* get_platform_wsi_extension(const char* api_selection) {
 #if defined(VK_USE_PLATFORM_ANDROID_KHR)
     return "VK_KHR_android_surface";
index bcc39bfeae0eee99e08ccfd3df5265307c012b90..a04c9e2e5c230e0e0eaa0dac26bd43758e7c6d2c 100644 (file)
@@ -456,6 +456,7 @@ enum class ManifestDiscoveryType {
     add_env_var,          // use the corresponding add-env-var for it
     override_folder,      // add to a special folder for the override layer to use
     windows_app_package,  // let the app package search find it
+    macos_bundle,         // place it in a location only accessible to macos bundles
 };
 
 struct TestICDDetails {
@@ -490,6 +491,7 @@ enum class ManifestLocation {
     implicit_layer = 6,
     override_layer = 7,
     windows_app_package = 8,
+    macos_bundle = 9,
 };
 
 struct FrameworkEnvironment {
@@ -516,7 +518,10 @@ struct FrameworkEnvironment {
     fs::path get_layer_manifest_path(size_t index = 0) noexcept;
 
     fs::FolderManager& get_folder(ManifestLocation location) noexcept;
-
+#if defined(__APPLE__)
+    // Set the path of the app bundle to the appropriate test framework bundle
+    void setup_macos_bundle() noexcept;
+#endif
     PlatformShimWrapper platform_shim;
     std::vector<fs::FolderManager> folders;
 
index 4e20eb62227a7d99a2b155d3714557a4d53feb1f..98aa3758d31568a26e1091764f2822ce5872ce19 100644 (file)
@@ -169,7 +169,7 @@ struct path {
     path operator/(std::string const& in) const;
     path operator/(const char* in) const;
 
-    // accesors
+    // accessors
     path parent_path() const;
     bool has_parent_path() const;
     path filename() const;
index 204a1c801e984c215d2984476492f1aa954173d0..0cbfc1430891686b0521787499ba07a62c70cb09 100644 (file)
@@ -2347,7 +2347,7 @@ TEST(EnumeratePhysicalDeviceGroups, FakePNext) {
     // NOTE: This is a fake struct to make sure the pNext chain is properly passed down to the ICD
     //       vkEnumeratePhysicalDeviceGroups.
     //       The two versions must match:
-    //           "FakePNext" test in loader_regresion_tests.cpp
+    //           "FakePNext" test in loader_regression_tests.cpp
     //           "test_vkEnumeratePhysicalDeviceGroups" in test_icd.cpp
     struct FakePnextSharedWithICD {
         VkStructureType sType;
@@ -3643,3 +3643,49 @@ TEST(ManifestDiscovery, InvalidSymlink) {
     inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER);
 }
 #endif
+
+#if defined(__APPLE__)
+// Add two drivers, one to the bundle and one to the system locations
+TEST(ManifestDiscovery, AppleBundles) {
+    FrameworkEnvironment env{};
+    env.setup_macos_bundle();
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_discovery_type(ManifestDiscoveryType::macos_bundle));
+    env.get_test_icd(0).physical_devices.push_back({});
+    env.get_test_icd(0).physical_devices.at(0).properties.deviceID = 1337;
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA));
+    env.get_test_icd(1).physical_devices.push_back({});
+    env.get_test_icd(1).physical_devices.at(0).properties.deviceID = 9999;
+
+    InstWrapper inst{env.vulkan_functions};
+    ASSERT_NO_FATAL_FAILURE(inst.CheckCreate());
+    auto physical_devices = inst.GetPhysDevs();
+    ASSERT_EQ(1, physical_devices.size());
+
+    // Verify that this is the 'right' GPU, aka the one from the bundle
+    VkPhysicalDeviceProperties props{};
+    inst->vkGetPhysicalDeviceProperties(physical_devices[0], &props);
+    ASSERT_EQ(env.get_test_icd(0).physical_devices.at(0).properties.deviceID, props.deviceID);
+}
+
+// Add two drivers, one to the bundle and one using the driver env-var
+TEST(ManifestDiscovery, AppleBundlesEnvVarActive) {
+    FrameworkEnvironment env{};
+    env.setup_macos_bundle();
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_discovery_type(ManifestDiscoveryType::macos_bundle));
+    env.get_test_icd(0).physical_devices.push_back({});
+    env.get_test_icd(0).physical_devices.at(0).properties.deviceID = 1337;
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_discovery_type(ManifestDiscoveryType::env_var));
+    env.get_test_icd(1).physical_devices.push_back({});
+    env.get_test_icd(1).physical_devices.at(0).properties.deviceID = 9999;
+
+    InstWrapper inst{env.vulkan_functions};
+    ASSERT_NO_FATAL_FAILURE(inst.CheckCreate());
+    auto physical_devices = inst.GetPhysDevs();
+    ASSERT_EQ(1, physical_devices.size());
+
+    // Verify that this is the 'right' GPU, aka the one from the env-var
+    VkPhysicalDeviceProperties props{};
+    inst->vkGetPhysicalDeviceProperties(physical_devices[0], &props);
+    ASSERT_EQ(env.get_test_icd(1).physical_devices.at(0).properties.deviceID, props.deviceID);
+}
+#endif