text: // Copyright 2019 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include <gtest/gtest.h>
  #include <vulkan/vulkan.h>
  
  const char* kXdgConfigDirs = "XDG_CONFIG_DIRS";
  
  class ValidationLayers : public testing::Test {
   public:
    // Set an environment variable for the life of the test case.
    void SetTemporaryEnv(const char* name, const char* value) {
      EXPECT_FALSE(getenv(name));
      setenv(name, value, true);
      environment_variables_.push_back(name);
    }
  
    void SetLayerPathEnviron() {
      if (getenv("VK_REQUIRE_LAYER_PATH")) {
        SetTemporaryEnv("VK_LAYER_PATH", "/vulkan_validation_pkg/data/vulkan/explicit_layer.d");
      }
    }
    void TearDown() override {
      for (const char* variable : environment_variables_) {
        unsetenv(variable);
      }
    }
  
   private:
    std::vector<const char*> environment_variables_;
  };
  
  TEST_F(ValidationLayers, InstanceLayers) {
    SetLayerPathEnviron();
    uint32_t layer_count;
    EXPECT_EQ(VK_SUCCESS, vkEnumerateInstanceLayerProperties(&layer_count, NULL));
    ASSERT_GE(layer_count, 1u);
  
    std::vector<VkLayerProperties> layers(layer_count);
    EXPECT_EQ(VK_SUCCESS, vkEnumerateInstanceLayerProperties(&layer_count, layers.data()));
  
    bool found_khronos_validation = false;
  
    for (auto& layer : layers) {
      if (strcmp(layer.layerName, "VK_LAYER_KHRONOS_validation") == 0)
        found_khronos_validation = true;
    }
  
    EXPECT_TRUE(found_khronos_validation);
  }
  
  namespace {
  
  VkBool32 debugMessageCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
                                VkDebugUtilsMessageTypeFlagsEXT messageTypes,
                                const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
                                void* pUserData) {
    if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) {
      auto validation_error_count = reinterpret_cast<uint32_t*>(pUserData);
      *validation_error_count += 1;
    }
    return VK_FALSE;
  }
  
  void test_validation_layer(const char* layer_name, bool explicit_validation) {
    uint32_t instance_extension_count;
    EXPECT_EQ(VK_SUCCESS,
              vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, nullptr));
    ASSERT_GE(instance_extension_count, 1u);
  
    std::vector<VkExtensionProperties> instance_extensions(instance_extension_count);
    EXPECT_EQ(VK_SUCCESS, vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count,
                                                                 instance_extensions.data()));
  
    bool found_debug_ext = false;
    for (auto& extension : instance_extensions) {
      if (strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, extension.extensionName) == 0) {
        found_debug_ext = true;
      }
    }
    ASSERT_TRUE(found_debug_ext);
  
    uint32_t validation_error_count = 0;
  
    VkDebugUtilsMessengerCreateInfoEXT debug_create_info = {
        .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
        .pNext = nullptr,
        .flags = 0,
        .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
                           VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
                           VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
                           VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
        .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
                       VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
                       VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
        .pfnUserCallback = debugMessageCallback,
        .pUserData = &validation_error_count,
    };
  
    struct VkApplicationInfo app_info = {.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
                                         .pNext = nullptr,
                                         .apiVersion = VK_API_VERSION_1_1};
  
    std::vector<const char*> layerNameArray;
    if (explicit_validation) {
      layerNameArray.push_back(layer_name);
      EXPECT_EQ(nullptr, getenv(kXdgConfigDirs));
    }
    std::vector<const char*> instanceExtensionArray{VK_EXT_DEBUG_UTILS_EXTENSION_NAME};
  
    VkInstanceCreateInfo inst_info = {
        .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
        .pNext = &debug_create_info,
        .pApplicationInfo = &app_info,
        .enabledLayerCount = static_cast<uint32_t>(layerNameArray.size()),
        .ppEnabledLayerNames = layerNameArray.data(),
        .enabledExtensionCount = static_cast<uint32_t>(instanceExtensionArray.size()),
        .ppEnabledExtensionNames = instanceExtensionArray.data(),
    };
  
    VkInstance vk_instance;
    ASSERT_EQ(VK_SUCCESS, vkCreateInstance(&inst_info, nullptr, &vk_instance));
  
    auto f_vkCreateDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
        vkGetInstanceProcAddr(vk_instance, "vkCreateDebugUtilsMessengerEXT"));
    ASSERT_TRUE(f_vkCreateDebugUtilsMessengerEXT);
  
    auto f_vkDestroyDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
        vkGetInstanceProcAddr(vk_instance, "vkDestroyDebugUtilsMessengerEXT"));
    ASSERT_TRUE(f_vkDestroyDebugUtilsMessengerEXT);
  
    VkDebugUtilsMessengerEXT messenger;
    ASSERT_EQ(VK_SUCCESS,
              f_vkCreateDebugUtilsMessengerEXT(vk_instance, &debug_create_info, nullptr, &messenger));
  
    uint32_t phys_device_count = 1;
    VkPhysicalDevice phys_device = VK_NULL_HANDLE;
    VkResult result = vkEnumeratePhysicalDevices(vk_instance, &phys_device_count, &phys_device);
    ASSERT_TRUE(result == VK_SUCCESS || result == VK_INCOMPLETE);
    if (result == VK_INCOMPLETE) {
      printf("vkEnumeratePhysicalDevices returned VK_INCOMPLETE: phys_device_count %u\n",
             phys_device_count);
    }
  
    uint32_t queue_family_count;
    vkGetPhysicalDeviceQueueFamilyProperties(phys_device, &queue_family_count, nullptr);
    ASSERT_GE(queue_family_count, 1u);
  
    std::vector<VkQueueFamilyProperties> queue_family_properties(queue_family_count);
    vkGetPhysicalDeviceQueueFamilyProperties(phys_device, &queue_family_count,
                                             queue_family_properties.data());
  
    int32_t queue_family_index = -1;
    for (uint32_t i = 0; i < queue_family_count; i++) {
      if (queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
        queue_family_index = i;
        break;
      }
    }
    ASSERT_GE(queue_family_index, 0);
  
    float queue_priorities[]{0.0f};
    VkDeviceQueueCreateInfo queue_create_info = {
        .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
        .pNext = nullptr,
        .flags = 0,
        .queueFamilyIndex = static_cast<uint32_t>(queue_family_index),
        .queueCount = 1,
        .pQueuePriorities = queue_priorities,
    };
  
    // This structure was selected because it's illegal to chain it onto VkDeviceCreateInfo, but it
    // doesn't cause any drivers we're using to assert or crash. Adding this structure is unlikely to
    // cause crashes in the future, since drivers are likely to ignore structures they don't
    // understand.
    VkBindSparseInfo bind_sparse_info = {
        .sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,
        .pNext = nullptr,
    };
    VkDeviceCreateInfo device_create_info = {
        .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
        .pNext = &bind_sparse_info,
        .queueCreateInfoCount = 1,
        .pQueueCreateInfos = &queue_create_info,
    };
    VkDevice vk_device;
    ASSERT_EQ(VK_SUCCESS, vkCreateDevice(phys_device, &device_create_info, nullptr, &vk_device));
  
    EXPECT_GE(validation_error_count, 1u);
  
    vkDestroyDevice(vk_device, nullptr);
    f_vkDestroyDebugUtilsMessengerEXT(vk_instance, messenger, nullptr);
    vkDestroyInstance(vk_instance, nullptr);
  }
  
  }  // namespace
  
  TEST_F(ValidationLayers, KhronosValidation) {
    SetLayerPathEnviron();
    test_validation_layer("VK_LAYER_KHRONOS_validation", /*explicit_validation=*/true);
  }
  
  // VkLayer_override.json in the package will be used to enable the validation
  // layers.
  TEST_F(ValidationLayers, KhronosValidationFromFile) {
    SetLayerPathEnviron();
    SetTemporaryEnv(kXdgConfigDirs, "/pkg/data/test-xdg");
    test_validation_layer("VK_LAYER_KHRONOS_validation", /*explicit_validation=*/false);
  }
  
  // VkLayer_override.json in the package will be used to enable the validation
  // layers and set the path to them
  TEST_F(ValidationLayers, KhronosValidationFromFileAndPath) {
    if (!getenv("VK_REQUIRE_LAYER_PATH")) {
      // Setting an explicit layer path won't work, so skip this test.
      GTEST_SKIP();
    }
    SetTemporaryEnv(kXdgConfigDirs, "/pkg/data/test-xdg2");
    test_validation_layer("VK_LAYER_KHRONOS_validation", /*explicit_validation=*/false);
  }
  