/*
 * Copyright (c) 2015-2017 The Khronos Group Inc.
 * Copyright (c) 2015-2017 Valve Corporation
 * Copyright (c) 2015-2017 LunarG, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and/or associated documentation files (the "Materials"), to
 * deal in the Materials without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Materials, and to permit persons to whom the Materials are
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice(s) and this permission notice shall be included in
 * all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
 * USE OR OTHER DEALINGS IN THE MATERIALS.
 *
 * Author: Jeremy Hayes <jeremy@lunarG.com>
 * Author: Mark Young <marky@lunarG.com>
 */

// Following items are needed for C++ to work with PRIxLEAST64
#define __STDC_FORMAT_MACROS
#include <inttypes.h>

#include <stdint.h>  // For UINT32_MAX

#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

#include "test_common.h"
#include <vulkan/vulkan.h>

namespace VK {

struct InstanceCreateInfo {
    InstanceCreateInfo()
        : info  // MSVC can't handle list initialization, thus explicit construction herein.
          (VkInstanceCreateInfo{
              VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,  // sType
              nullptr,                                 // pNext
              0,                                       // flags
              nullptr,                                 // pApplicationInfo
              0,                                       // enabledLayerCount
              nullptr,                                 // ppEnabledLayerNames
              0,                                       // enabledExtensionCount
              nullptr                                  // ppEnabledExtensionNames
          }) {}

    InstanceCreateInfo &sType(VkStructureType const &sType) {
        info.sType = sType;

        return *this;
    }

    InstanceCreateInfo &pNext(void const *const pNext) {
        info.pNext = pNext;

        return *this;
    }

    InstanceCreateInfo &flags(VkInstanceCreateFlags const &flags) {
        info.flags = flags;

        return *this;
    }

    InstanceCreateInfo &pApplicationInfo(VkApplicationInfo const *const pApplicationInfo) {
        info.pApplicationInfo = pApplicationInfo;

        return *this;
    }

    InstanceCreateInfo &enabledLayerCount(uint32_t const &enabledLayerCount) {
        info.enabledLayerCount = enabledLayerCount;

        return *this;
    }

    InstanceCreateInfo &ppEnabledLayerNames(char const *const *const ppEnabledLayerNames) {
        info.ppEnabledLayerNames = ppEnabledLayerNames;

        return *this;
    }

    InstanceCreateInfo &enabledExtensionCount(uint32_t const &enabledExtensionCount) {
        info.enabledExtensionCount = enabledExtensionCount;

        return *this;
    }

    InstanceCreateInfo &ppEnabledExtensionNames(char const *const *const ppEnabledExtensionNames) {
        info.ppEnabledExtensionNames = ppEnabledExtensionNames;

        return *this;
    }

    operator VkInstanceCreateInfo const *() const { return &info; }

    operator VkInstanceCreateInfo *() { return &info; }

    VkInstanceCreateInfo info;
};

struct DeviceQueueCreateInfo {
    DeviceQueueCreateInfo()
        : info  // MSVC can't handle list initialization, thus explicit construction herein.
          (VkDeviceQueueCreateInfo{
              VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,  // sType
              nullptr,                                     // pNext
              0,                                           // flags
              0,                                           // queueFamilyIndex
              0,                                           // queueCount
              nullptr                                      // pQueuePriorities
          }) {}

    DeviceQueueCreateInfo &sType(VkStructureType const &sType) {
        info.sType = sType;

        return *this;
    }

    DeviceQueueCreateInfo &pNext(void const *const pNext) {
        info.pNext = pNext;

        return *this;
    }

    DeviceQueueCreateInfo &flags(VkDeviceQueueCreateFlags const &flags) {
        info.flags = flags;

        return *this;
    }

    DeviceQueueCreateInfo &queueFamilyIndex(uint32_t const &queueFamilyIndex) {
        info.queueFamilyIndex = queueFamilyIndex;

        return *this;
    }

    DeviceQueueCreateInfo &queueCount(uint32_t const &queueCount) {
        info.queueCount = queueCount;

        return *this;
    }

    DeviceQueueCreateInfo &pQueuePriorities(float const *const pQueuePriorities) {
        info.pQueuePriorities = pQueuePriorities;

        return *this;
    }

    operator VkDeviceQueueCreateInfo() { return info; }

    VkDeviceQueueCreateInfo info;
};

struct DeviceCreateInfo {
    DeviceCreateInfo()
        : info  // MSVC can't handle list initialization, thus explicit construction herein.
          (VkDeviceCreateInfo{
              VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,  // sType
              nullptr,                               // pNext
              0,                                     // flags
              0,                                     // queueCreateInfoCount
              nullptr,                               // pQueueCreateInfos
              0,                                     // enabledLayerCount
              nullptr,                               // ppEnabledLayerNames
              0,                                     // enabledExtensionCount
              nullptr,                               // ppEnabledExtensionNames
              nullptr                                // pEnabledFeatures
          }) {}

    DeviceCreateInfo &sType(VkStructureType const &sType) {
        info.sType = sType;

        return *this;
    }

    DeviceCreateInfo &pNext(void const *const pNext) {
        info.pNext = pNext;

        return *this;
    }

    DeviceCreateInfo &flags(VkDeviceQueueCreateFlags const &flags) {
        info.flags = flags;

        return *this;
    }

    DeviceCreateInfo &queueCreateInfoCount(uint32_t const &queueCreateInfoCount) {
        info.queueCreateInfoCount = queueCreateInfoCount;

        return *this;
    }

    DeviceCreateInfo &pQueueCreateInfos(VkDeviceQueueCreateInfo const *const pQueueCreateInfos) {
        info.pQueueCreateInfos = pQueueCreateInfos;

        return *this;
    }

    DeviceCreateInfo &enabledLayerCount(uint32_t const &enabledLayerCount) {
        info.enabledLayerCount = enabledLayerCount;

        return *this;
    }

    DeviceCreateInfo &ppEnabledLayerNames(char const *const *const ppEnabledLayerNames) {
        info.ppEnabledLayerNames = ppEnabledLayerNames;

        return *this;
    }

    DeviceCreateInfo &enabledExtensionCount(uint32_t const &enabledExtensionCount) {
        info.enabledExtensionCount = enabledExtensionCount;

        return *this;
    }

    DeviceCreateInfo &ppEnabledExtensionNames(char const *const *const ppEnabledExtensionNames) {
        info.ppEnabledExtensionNames = ppEnabledExtensionNames;

        return *this;
    }

    DeviceCreateInfo &pEnabledFeatures(VkPhysicalDeviceFeatures const *const pEnabledFeatures) {
        info.pEnabledFeatures = pEnabledFeatures;

        return *this;
    }

    operator VkDeviceCreateInfo const *() const { return &info; }

    operator VkDeviceCreateInfo *() { return &info; }

    VkDeviceCreateInfo info;
};
}  // namespace VK

struct CommandLine : public ::testing::Test {
    static void Initialize(int argc, char **argv) { arguments.assign(argv, argv + argc); };

    static void SetUpTestCase(){};
    static void TearDownTestCase(){};

    static std::vector<std::string> arguments;
};
std::vector<std::string> CommandLine::arguments;

struct EnumerateInstanceLayerProperties : public CommandLine {};
struct EnumerateInstanceExtensionProperties : public CommandLine {};
struct ImplicitLayer : public CommandLine {};

// Allocation tracking utilities
struct AllocTrack {
    bool active;
    bool was_allocated;
    void *aligned_start_addr;
    char *actual_start_addr;
    size_t requested_size_bytes;
    size_t actual_size_bytes;
    VkSystemAllocationScope alloc_scope;
    uint64_t user_data;

    AllocTrack()
        : active(false),
          was_allocated(false),
          aligned_start_addr(nullptr),
          actual_start_addr(nullptr),
          requested_size_bytes(0),
          actual_size_bytes(0),
          alloc_scope(VK_SYSTEM_ALLOCATION_SCOPE_COMMAND),
          user_data(0) {}
};

// Global vector to track allocations.  This will be resized before each test and emptied after.
// However, we have to globally define it so the allocation callback functions work properly.
std::vector<AllocTrack> g_allocated_vector;
bool g_intentional_fail_enabled = false;
uint32_t g_intenional_fail_index = 0;
uint32_t g_intenional_fail_count = 0;

void FreeAllocTracker() { g_allocated_vector.clear(); }

void InitAllocTracker(size_t size, uint32_t intentional_fail_index = UINT32_MAX) {
    if (g_allocated_vector.size() > 0) {
        FreeAllocTracker();
    }
    g_allocated_vector.resize(size);
    if (intentional_fail_index != UINT32_MAX) {
        g_intentional_fail_enabled = true;
        g_intenional_fail_index = intentional_fail_index;
        g_intenional_fail_count = 0;
    } else {
        g_intentional_fail_enabled = false;
        g_intenional_fail_index = 0;
        g_intenional_fail_count = 0;
    }
}

bool IsAllocTrackerEmpty() {
    bool success = true;
    bool was_allocated = false;
    char print_command[1024];
    sprintf(print_command, "\t%%04d\t%%p (%%p) : 0x%%%s (0x%%%s) : scope %%d : user_data 0x%%%s\n", PRIxLEAST64, PRIxLEAST64,
            PRIxLEAST64);
    for (uint32_t iii = 0; iii < g_allocated_vector.size(); iii++) {
        if (g_allocated_vector[iii].active) {
            if (success) {
                printf("ERROR: Allocations still remain!\n");
            }
            printf(print_command, iii, g_allocated_vector[iii].aligned_start_addr, g_allocated_vector[iii].actual_start_addr,
                   g_allocated_vector[iii].requested_size_bytes, g_allocated_vector[iii].actual_size_bytes,
                   g_allocated_vector[iii].alloc_scope, g_allocated_vector[iii].user_data);
            success = false;
        } else if (!was_allocated && g_allocated_vector[iii].was_allocated) {
            was_allocated = true;
        }
    }
    if (!g_intentional_fail_enabled && !was_allocated) {
        printf("No allocations ever generated!");
        success = false;
    }
    return success;
}

VKAPI_ATTR void *VKAPI_CALL AllocCallbackFunc(void *pUserData, size_t size, size_t alignment,
                                              VkSystemAllocationScope allocationScope) {
    if (g_intentional_fail_enabled) {
        if (++g_intenional_fail_count >= g_intenional_fail_index) {
            return nullptr;
        }
    }
    for (uint32_t iii = 0; iii < g_allocated_vector.size(); iii++) {
        if (!g_allocated_vector[iii].active) {
            g_allocated_vector[iii].requested_size_bytes = size;
            g_allocated_vector[iii].actual_size_bytes = size + (alignment - 1);
            g_allocated_vector[iii].aligned_start_addr = NULL;
            g_allocated_vector[iii].actual_start_addr = new char[g_allocated_vector[iii].actual_size_bytes];
            if (g_allocated_vector[iii].actual_start_addr != NULL) {
                uint64_t addr = (uint64_t)g_allocated_vector[iii].actual_start_addr;
                addr += (alignment - 1);
                addr &= ~(alignment - 1);
                g_allocated_vector[iii].aligned_start_addr = (void *)addr;
                g_allocated_vector[iii].alloc_scope = allocationScope;
                g_allocated_vector[iii].user_data = (uint64_t)pUserData;
                g_allocated_vector[iii].active = true;
                g_allocated_vector[iii].was_allocated = true;
            }
            return g_allocated_vector[iii].aligned_start_addr;
        }
    }
    return nullptr;
}

VKAPI_ATTR void VKAPI_CALL FreeCallbackFunc(void *pUserData, void *pMemory) {
    for (uint32_t iii = 0; iii < g_allocated_vector.size(); iii++) {
        if (g_allocated_vector[iii].active && g_allocated_vector[iii].aligned_start_addr == pMemory) {
            delete[] g_allocated_vector[iii].actual_start_addr;
            g_allocated_vector[iii].active = false;
            break;
        }
    }
}

VKAPI_ATTR void *VKAPI_CALL ReallocCallbackFunc(void *pUserData, void *pOriginal, size_t size, size_t alignment,
                                                VkSystemAllocationScope allocationScope) {
    if (pOriginal != NULL) {
        for (uint32_t iii = 0; iii < g_allocated_vector.size(); iii++) {
            if (g_allocated_vector[iii].active && g_allocated_vector[iii].aligned_start_addr == pOriginal) {
                if (size == 0) {
                    FreeCallbackFunc(pUserData, pOriginal);
                    return nullptr;
                } else if (size < g_allocated_vector[iii].requested_size_bytes) {
                    return pOriginal;
                } else {
                    void *pNew = AllocCallbackFunc(pUserData, size, alignment, allocationScope);
                    if (pNew != NULL) {
                        size_t copy_size = size;
                        if (g_allocated_vector[iii].requested_size_bytes < size) {
                            copy_size = g_allocated_vector[iii].requested_size_bytes;
                        }
                        memcpy(pNew, pOriginal, copy_size);
                        FreeCallbackFunc(pUserData, pOriginal);
                    }
                    return pNew;
                }
            }
        }
        return nullptr;
    } else {
        return AllocCallbackFunc(pUserData, size, alignment, allocationScope);
    }
}

// Test groups:
// LX = lunar exchange
// LVLGH = loader and validation github
// LVLGL = lodaer and validation gitlab

TEST(LX435, InstanceCreateInfoConst) {
    VkInstanceCreateInfo const info = {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, nullptr, 0, nullptr, 0, nullptr, 0, nullptr};

    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(&info, VK_NULL_HANDLE, &instance);
    EXPECT_EQ(result, VK_SUCCESS);

    vkDestroyInstance(instance, nullptr);
}

TEST(LX475, DestroyInstanceNullHandle) { vkDestroyInstance(VK_NULL_HANDLE, nullptr); }

TEST(LX475, DestroyDeviceNullHandle) { vkDestroyDevice(VK_NULL_HANDLE, nullptr); }

TEST(CreateInstance, ExtensionNotPresent) {
    char const *const names[] = {"NotPresent"};  // Temporary required due to MSVC bug.
    auto const info = VK::InstanceCreateInfo().enabledExtensionCount(1).ppEnabledExtensionNames(names);

    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(info, VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_ERROR_EXTENSION_NOT_PRESENT);

    // It's not necessary to destroy the instance because it will not be created successfully.
}

TEST(CreateInstance, LayerNotPresent) {
    char const *const names[] = {"NotPresent"};  // Temporary required due to MSVC bug.
    auto const info = VK::InstanceCreateInfo().enabledLayerCount(1).ppEnabledLayerNames(names);

    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(info, VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_ERROR_LAYER_NOT_PRESENT);

    // It's not necessary to destroy the instance because it will not be created successfully.
}

// Used by run_loader_tests.sh to test for layer insertion.
TEST(CreateInstance, LayerPresent) {
    char const *const names1[] = {"VK_LAYER_LUNARG_parameter_validation"};  // Temporary required due to MSVC bug.
    char const *const names2[] = {"VK_LAYER_LUNARG_standard_validation"};   // Temporary required due to MSVC bug.
    auto const info1 = VK::InstanceCreateInfo().enabledLayerCount(1).ppEnabledLayerNames(names1);
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(info1, VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);
    vkDestroyInstance(instance, nullptr);

    auto const info2 = VK::InstanceCreateInfo().enabledLayerCount(1).ppEnabledLayerNames(names2);
    instance = VK_NULL_HANDLE;
    result = vkCreateInstance(info2, VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);
    vkDestroyInstance(instance, nullptr);
}

// Used by run_loader_tests.sh to test that calling vkEnumeratePhysicalDevices without first querying
// the count, works.
TEST(EnumeratePhysicalDevices, OneCall) {
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(VK::InstanceCreateInfo(), VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 500;
    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    vkDestroyInstance(instance, nullptr);
}

// Used by run_loader_tests.sh to test for the expected usage of the vkEnumeratePhysicalDevices call.
TEST(EnumeratePhysicalDevices, TwoCall) {
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(VK::InstanceCreateInfo(), VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    vkDestroyInstance(instance, nullptr);
}

// Used by run_loader_tests.sh to test that calling vkEnumeratePhysicalDevices without first querying
// the count, matches the count from the standard call.
TEST(EnumeratePhysicalDevices, MatchOneAndTwoCallNumbers) {
    VkInstance instance_one = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(VK::InstanceCreateInfo(), VK_NULL_HANDLE, &instance_one);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount_one = 500;
    std::unique_ptr<VkPhysicalDevice[]> physical_one(new VkPhysicalDevice[physicalCount_one]);
    result = vkEnumeratePhysicalDevices(instance_one, &physicalCount_one, physical_one.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount_one, 0u);

    VkInstance instance_two = VK_NULL_HANDLE;
    result = vkCreateInstance(VK::InstanceCreateInfo(), VK_NULL_HANDLE, &instance_two);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount_two = 0;
    result = vkEnumeratePhysicalDevices(instance_two, &physicalCount_two, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount_two, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical_two(new VkPhysicalDevice[physicalCount_two]);
    result = vkEnumeratePhysicalDevices(instance_two, &physicalCount_two, physical_two.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount_two, 0u);

    ASSERT_EQ(physicalCount_one, physicalCount_two);

    vkDestroyInstance(instance_one, nullptr);
    vkDestroyInstance(instance_two, nullptr);
}

// Used by run_loader_tests.sh to test for the expected usage of the vkEnumeratePhysicalDevices
// call if not enough numbers are provided for the final list.
TEST(EnumeratePhysicalDevices, TwoCallIncomplete) {
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(VK::InstanceCreateInfo(), VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);

    // Remove one from the physical device count so we can get the VK_INCOMPLETE message
    physicalCount -= 1;

    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_INCOMPLETE);

    vkDestroyInstance(instance, nullptr);
}

// Test to make sure that layers enabled in the instance show up in the list of device layers.
TEST(EnumerateDeviceLayers, LayersMatch) {
    char const *const names1[] = {"VK_LAYER_LUNARG_standard_validation"};
    char const *const names2[3] = {"VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_core_validation",
                                   "VK_LAYER_LUNARG_object_tracker"};
    auto const info1 = VK::InstanceCreateInfo().enabledLayerCount(1).ppEnabledLayerNames(names1);
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(info1, VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);
    uint32_t count = 24;
    VkLayerProperties layer_props[24];
    vkEnumerateDeviceLayerProperties(physical[0], &count, layer_props);
    ASSERT_GE(count, 1u);
    bool found = false;
    for (uint32_t iii = 0; iii < count; iii++) {
        if (!strcmp(layer_props[iii].layerName, names1[0])) {
            found = true;
            break;
        }
    }
    if (!found) {
        ASSERT_EQ(count, 0);
    }

    vkDestroyInstance(instance, nullptr);

    auto const info2 = VK::InstanceCreateInfo().enabledLayerCount(3).ppEnabledLayerNames(names2);
    instance = VK_NULL_HANDLE;
    result = vkCreateInstance(info2, VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical2(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical2.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    count = 24;
    vkEnumerateDeviceLayerProperties(physical2[0], &count, layer_props);
    ASSERT_GE(count, 3u);
    for (uint32_t jjj = 0; jjj < 3; jjj++) {
        found = false;
        for (uint32_t iii = 0; iii < count; iii++) {
            if (!strcmp(layer_props[iii].layerName, names2[jjj])) {
                found = true;
                break;
            }
        }
        if (!found) {
            ASSERT_EQ(count, 0);
        }
    }

    vkDestroyInstance(instance, nullptr);
}

TEST(CreateDevice, ExtensionNotPresent) {
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(VK::InstanceCreateInfo(), VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    for (uint32_t p = 0; p < physicalCount; ++p) {
        uint32_t familyCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, nullptr);
        ASSERT_GT(familyCount, 0u);

        std::unique_ptr<VkQueueFamilyProperties[]> family(new VkQueueFamilyProperties[familyCount]);
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, family.get());
        ASSERT_GT(familyCount, 0u);

        for (uint32_t q = 0; q < familyCount; ++q) {
            if (~family[q].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                continue;
            }

            float const priorities[] = {0.0f};  // Temporary required due to MSVC bug.
            VkDeviceQueueCreateInfo const queueInfo[1]{
                VK::DeviceQueueCreateInfo().queueFamilyIndex(q).queueCount(1).pQueuePriorities(priorities)};

            char const *const names[] = {"NotPresent"};  // Temporary required due to MSVC bug.
            auto const deviceInfo = VK::DeviceCreateInfo()
                                        .queueCreateInfoCount(1)
                                        .pQueueCreateInfos(queueInfo)
                                        .enabledExtensionCount(1)
                                        .ppEnabledExtensionNames(names);

            VkDevice device;
            result = vkCreateDevice(physical[p], deviceInfo, nullptr, &device);
            ASSERT_EQ(result, VK_ERROR_EXTENSION_NOT_PRESENT);

            // It's not necessary to destroy the device because it will not be created successfully.
        }
    }

    vkDestroyInstance(instance, nullptr);
}

// LX535 / MI-76: Device layers are deprecated.
// For backwards compatibility, they are allowed, but must be ignored.
// Ensure that no errors occur if a bogus device layer list is passed to vkCreateDevice.
TEST(CreateDevice, LayersNotPresent) {
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(VK::InstanceCreateInfo(), VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    for (uint32_t p = 0; p < physicalCount; ++p) {
        uint32_t familyCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, nullptr);
        ASSERT_EQ(result, VK_SUCCESS);
        ASSERT_GT(familyCount, 0u);

        std::unique_ptr<VkQueueFamilyProperties[]> family(new VkQueueFamilyProperties[familyCount]);
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, family.get());
        ASSERT_EQ(result, VK_SUCCESS);
        ASSERT_GT(familyCount, 0u);

        for (uint32_t q = 0; q < familyCount; ++q) {
            if (~family[q].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                continue;
            }

            float const priorities[] = {0.0f};  // Temporary required due to MSVC bug.
            VkDeviceQueueCreateInfo const queueInfo[1]{
                VK::DeviceQueueCreateInfo().queueFamilyIndex(q).queueCount(1).pQueuePriorities(priorities)};

            char const *const names[] = {"NotPresent"};  // Temporary required due to MSVC bug.
            auto const deviceInfo = VK::DeviceCreateInfo()
                                        .queueCreateInfoCount(1)
                                        .pQueueCreateInfos(queueInfo)
                                        .enabledLayerCount(1)
                                        .ppEnabledLayerNames(names);

            VkDevice device;
            result = vkCreateDevice(physical[p], deviceInfo, nullptr, &device);
            ASSERT_EQ(result, VK_SUCCESS);

            vkDestroyDevice(device, nullptr);
        }
    }

    vkDestroyInstance(instance, nullptr);
}

TEST_F(EnumerateInstanceLayerProperties, PropertyCountLessThanAvailable) {
    uint32_t count = 0u;
    VkResult result = vkEnumerateInstanceLayerProperties(&count, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);

    // We need atleast two for the test to be relevant.
    if (count < 2u) {
        return;
    }

    std::unique_ptr<VkLayerProperties[]> properties(new VkLayerProperties[count]);
    count = 1;
    result = vkEnumerateInstanceLayerProperties(&count, properties.get());
    ASSERT_EQ(result, VK_INCOMPLETE);
}

TEST(EnumerateDeviceLayerProperties, PropertyCountLessThanAvailable) {
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(VK::InstanceCreateInfo(), VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    for (uint32_t p = 0; p < physicalCount; ++p) {
        uint32_t count = 0u;
        result = vkEnumerateDeviceLayerProperties(physical[p], &count, nullptr);
        ASSERT_EQ(result, VK_SUCCESS);

        // We need atleast two for the test to be relevant.
        if (count < 2u) {
            continue;
        }

        std::unique_ptr<VkLayerProperties[]> properties(new VkLayerProperties[count]);
        count = 1;
        result = vkEnumerateDeviceLayerProperties(physical[p], &count, properties.get());
        ASSERT_EQ(result, VK_INCOMPLETE);
    }

    vkDestroyInstance(instance, nullptr);
}

TEST_F(EnumerateInstanceLayerProperties, Count) {
    uint32_t count = 0u;
    VkResult result = vkEnumerateInstanceLayerProperties(&count, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);

    if (std::find(arguments.begin(), arguments.end(), "count") != arguments.end()) {
        std::cout << "count=" << count << '\n';
    }
}

TEST_F(EnumerateInstanceLayerProperties, OnePass) {
    // Count required for this test.
    if (std::find(arguments.begin(), arguments.end(), "count") == arguments.end()) {
        return;
    }

    uint32_t count = std::stoul(arguments[2]);

    std::unique_ptr<VkLayerProperties[]> properties(new VkLayerProperties[count]);
    VkResult result = vkEnumerateInstanceLayerProperties(&count, properties.get());
    ASSERT_EQ(result, VK_SUCCESS);

    if (std::find(arguments.begin(), arguments.end(), "properties") != arguments.end()) {
        for (uint32_t p = 0; p < count; ++p) {
            std::cout << "properties[" << p << "] =" << ' ' << properties[p].layerName << ' ' << properties[p].specVersion << ' '
                      << properties[p].implementationVersion << ' ' << properties[p].description << '\n';
        }
    }
}

TEST_F(EnumerateInstanceLayerProperties, TwoPass) {
    uint32_t count = 0u;
    VkResult result = vkEnumerateInstanceLayerProperties(&count, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);

    std::unique_ptr<VkLayerProperties[]> properties(new VkLayerProperties[count]);
    result = vkEnumerateInstanceLayerProperties(&count, properties.get());
    ASSERT_EQ(result, VK_SUCCESS);

    if (std::find(arguments.begin(), arguments.end(), "properties") != arguments.end()) {
        for (uint32_t p = 0; p < count; ++p) {
            std::cout << "properties[" << p << "] =" << ' ' << properties[p].layerName << ' ' << properties[p].specVersion << ' '
                      << properties[p].implementationVersion << ' ' << properties[p].description << '\n';
        }
    }
}

TEST_F(EnumerateInstanceExtensionProperties, PropertyCountLessThanAvailable) {
    uint32_t count = 0u;
    VkResult result = vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);

    // We need atleast two for the test to be relevant.
    if (count < 2u) {
        return;
    }

    std::unique_ptr<VkExtensionProperties[]> properties(new VkExtensionProperties[count]);
    count = 1;
    result = vkEnumerateInstanceExtensionProperties(nullptr, &count, properties.get());
    ASSERT_EQ(result, VK_INCOMPLETE);
}

TEST(EnumerateDeviceExtensionProperties, PropertyCountLessThanAvailable) {
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(VK::InstanceCreateInfo(), VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    for (uint32_t p = 0; p < physicalCount; ++p) {
        uint32_t count = 0u;
        result = vkEnumerateDeviceExtensionProperties(physical[p], nullptr, &count, nullptr);
        ASSERT_EQ(result, VK_SUCCESS);

        // We need atleast two for the test to be relevant.
        if (count < 2u) {
            continue;
        }

        std::unique_ptr<VkExtensionProperties[]> properties(new VkExtensionProperties[count]);
        count = 1;
        result = vkEnumerateDeviceExtensionProperties(physical[p], nullptr, &count, properties.get());
        ASSERT_EQ(result, VK_INCOMPLETE);
    }

    vkDestroyInstance(instance, nullptr);
}

TEST_F(EnumerateInstanceExtensionProperties, Count) {
    uint32_t count = 0u;
    VkResult result = vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);

    if (std::find(arguments.begin(), arguments.end(), "count") != arguments.end()) {
        std::cout << "count=" << count << '\n';
    }
}

TEST_F(EnumerateInstanceExtensionProperties, OnePass) {
    // Count required for this test.
    if (std::find(arguments.begin(), arguments.end(), "count") == arguments.end()) {
        return;
    }

    uint32_t count = std::stoul(arguments[2]);

    std::unique_ptr<VkExtensionProperties[]> properties(new VkExtensionProperties[count]);
    VkResult result = vkEnumerateInstanceExtensionProperties(nullptr, &count, properties.get());
    ASSERT_EQ(result, VK_SUCCESS);

    if (std::find(arguments.begin(), arguments.end(), "properties") != arguments.end()) {
        for (uint32_t p = 0; p < count; ++p) {
            std::cout << "properties[" << p << "] =" << ' ' << properties[p].extensionName << ' ' << properties[p].specVersion
                      << '\n';
        }
    }
}

TEST_F(EnumerateInstanceExtensionProperties, TwoPass) {
    uint32_t count = 0u;
    VkResult result = vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);

    std::unique_ptr<VkExtensionProperties[]> properties(new VkExtensionProperties[count]);
    result = vkEnumerateInstanceExtensionProperties(nullptr, &count, properties.get());
    ASSERT_EQ(result, VK_SUCCESS);

    if (std::find(arguments.begin(), arguments.end(), "properties") != arguments.end()) {
        for (uint32_t p = 0; p < count; ++p) {
            std::cout << "properties[" << p << "] =" << ' ' << properties[p].extensionName << ' ' << properties[p].specVersion
                      << '\n';
        }
    }
}

TEST_F(EnumerateInstanceExtensionProperties, InstanceExtensionEnumerated) {
    uint32_t count = 0u;
    VkResult result = vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);

    std::unique_ptr<VkExtensionProperties[]> properties(new VkExtensionProperties[count]);
    result = vkEnumerateInstanceExtensionProperties(nullptr, &count, properties.get());
    ASSERT_EQ(result, VK_SUCCESS);

    ASSERT_NE(std::find_if(
                  &properties[0], &properties[count],
                  [](VkExtensionProperties const &properties) { return strcmp(properties.extensionName, "VK_KHR_surface") == 0; }),
              &properties[count]);
}

TEST(EnumerateDeviceExtensionProperties, DeviceExtensionEnumerated) {
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(VK::InstanceCreateInfo(), VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    for (uint32_t p = 0; p < physicalCount; ++p) {
        uint32_t count = 0u;
        result = vkEnumerateDeviceExtensionProperties(physical[p], nullptr, &count, nullptr);
        ASSERT_EQ(result, VK_SUCCESS);

        std::unique_ptr<VkExtensionProperties[]> properties(new VkExtensionProperties[count]);
        result = vkEnumerateDeviceExtensionProperties(physical[p], nullptr, &count, properties.get());
        ASSERT_EQ(result, VK_SUCCESS);

        ASSERT_NE(std::find_if(&properties[0], &properties[count],
                               [](VkExtensionProperties const &properties) {
                                   return strcmp(properties.extensionName, "VK_KHR_swapchain") == 0;
                               }),
                  &properties[count]);
    }

    vkDestroyInstance(instance, nullptr);
}

TEST_F(ImplicitLayer, Present) {
    auto const info = VK::InstanceCreateInfo();
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(info, VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    vkDestroyInstance(instance, nullptr);
}

TEST(WrapObjects, Insert) {
    VkInstance instance = VK_NULL_HANDLE;
    VkResult result = vkCreateInstance(VK::InstanceCreateInfo(), VK_NULL_HANDLE, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    for (uint32_t p = 0; p < physicalCount; ++p) {
        uint32_t familyCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, nullptr);
        ASSERT_EQ(result, VK_SUCCESS);
        ASSERT_GT(familyCount, 0u);

        std::unique_ptr<VkQueueFamilyProperties[]> family(new VkQueueFamilyProperties[familyCount]);
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, family.get());
        ASSERT_EQ(result, VK_SUCCESS);
        ASSERT_GT(familyCount, 0u);

        for (uint32_t q = 0; q < familyCount; ++q) {
            if (~family[q].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                continue;
            }

            float const priorities[] = {0.0f};  // Temporary required due to MSVC bug.
            VkDeviceQueueCreateInfo const queueInfo[1]{
                VK::DeviceQueueCreateInfo().queueFamilyIndex(q).queueCount(1).pQueuePriorities(priorities)};

            auto const deviceInfo = VK::DeviceCreateInfo().queueCreateInfoCount(1).pQueueCreateInfos(queueInfo);

            VkDevice device;
            result = vkCreateDevice(physical[p], deviceInfo, nullptr, &device);
            ASSERT_EQ(result, VK_SUCCESS);

            vkDestroyDevice(device, nullptr);
        }
    }

    vkDestroyInstance(instance, nullptr);
}

// Test making sure the allocation functions are called to allocate and cleanup everything during
// a CreateInstance/DestroyInstance call pair.
TEST(Allocation, Instance) {
    auto const info = VK::InstanceCreateInfo();
    VkInstance instance = VK_NULL_HANDLE;
    VkAllocationCallbacks alloc_callbacks = {};
    alloc_callbacks.pUserData = (void *)0x00000001;
    alloc_callbacks.pfnAllocation = AllocCallbackFunc;
    alloc_callbacks.pfnReallocation = ReallocCallbackFunc;
    alloc_callbacks.pfnFree = FreeCallbackFunc;

    InitAllocTracker(2048);

    VkResult result = vkCreateInstance(info, &alloc_callbacks, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    alloc_callbacks.pUserData = (void *)0x00000002;
    vkDestroyInstance(instance, &alloc_callbacks);

    // Make sure everything's been freed
    ASSERT_EQ(true, IsAllocTrackerEmpty());
    FreeAllocTracker();
}

// Test making sure the allocation functions are called to allocate and cleanup everything during
// a CreateInstance/DestroyInstance call pair with a call to GetInstanceProcAddr.
TEST(Allocation, GetInstanceProcAddr) {
    auto const info = VK::InstanceCreateInfo();
    VkInstance instance = VK_NULL_HANDLE;
    VkAllocationCallbacks alloc_callbacks = {};
    alloc_callbacks.pUserData = (void *)0x00000010;
    alloc_callbacks.pfnAllocation = AllocCallbackFunc;
    alloc_callbacks.pfnReallocation = ReallocCallbackFunc;
    alloc_callbacks.pfnFree = FreeCallbackFunc;

    InitAllocTracker(2048);

    VkResult result = vkCreateInstance(info, &alloc_callbacks, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    void *pfnCreateDevice = (void *)vkGetInstanceProcAddr(instance, "vkCreateDevice");
    void *pfnDestroyDevice = (void *)vkGetInstanceProcAddr(instance, "vkDestroyDevice");
    ASSERT_TRUE(pfnCreateDevice != NULL && pfnDestroyDevice != NULL);

    alloc_callbacks.pUserData = (void *)0x00000011;
    vkDestroyInstance(instance, &alloc_callbacks);

    // Make sure everything's been freed
    ASSERT_EQ(true, IsAllocTrackerEmpty());
    FreeAllocTracker();
}

// Test making sure the allocation functions are called to allocate and cleanup everything during
// a vkEnumeratePhysicalDevices call pair.
TEST(Allocation, EnumeratePhysicalDevices) {
    auto const info = VK::InstanceCreateInfo();
    VkInstance instance = VK_NULL_HANDLE;
    VkAllocationCallbacks alloc_callbacks = {};
    alloc_callbacks.pUserData = (void *)0x00000021;
    alloc_callbacks.pfnAllocation = AllocCallbackFunc;
    alloc_callbacks.pfnReallocation = ReallocCallbackFunc;
    alloc_callbacks.pfnFree = FreeCallbackFunc;

    InitAllocTracker(2048);

    VkResult result = vkCreateInstance(info, &alloc_callbacks, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    alloc_callbacks.pUserData = (void *)0x00000022;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    alloc_callbacks.pUserData = (void *)0x00000023;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    alloc_callbacks.pUserData = (void *)0x00000024;
    vkDestroyInstance(instance, &alloc_callbacks);

    // Make sure everything's been freed
    ASSERT_EQ(true, IsAllocTrackerEmpty());
    FreeAllocTracker();
}

// Test making sure the allocation functions are called to allocate and cleanup everything from
// vkCreateInstance, to vkCreateDevicce, and then through their destructors.  With special
// allocators used on both the instance and device.
TEST(Allocation, InstanceAndDevice) {
    auto const info = VK::InstanceCreateInfo();
    VkInstance instance = VK_NULL_HANDLE;
    VkAllocationCallbacks alloc_callbacks = {};
    alloc_callbacks.pUserData = (void *)0x00000031;
    alloc_callbacks.pfnAllocation = AllocCallbackFunc;
    alloc_callbacks.pfnReallocation = ReallocCallbackFunc;
    alloc_callbacks.pfnFree = FreeCallbackFunc;

    InitAllocTracker(2048);

    VkResult result = vkCreateInstance(info, &alloc_callbacks, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    for (uint32_t p = 0; p < physicalCount; ++p) {
        uint32_t familyCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, nullptr);
        ASSERT_GT(familyCount, 0u);

        std::unique_ptr<VkQueueFamilyProperties[]> family(new VkQueueFamilyProperties[familyCount]);
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, family.get());
        ASSERT_GT(familyCount, 0u);

        for (uint32_t q = 0; q < familyCount; ++q) {
            if (~family[q].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                continue;
            }

            float const priorities[] = {0.0f};  // Temporary required due to MSVC bug.
            VkDeviceQueueCreateInfo const queueInfo[1]{
                VK::DeviceQueueCreateInfo().queueFamilyIndex(q).queueCount(1).pQueuePriorities(priorities)};

            auto const deviceInfo = VK::DeviceCreateInfo().queueCreateInfoCount(1).pQueueCreateInfos(queueInfo);

            VkDevice device;
            alloc_callbacks.pUserData = (void *)0x00000032;
            result = vkCreateDevice(physical[p], deviceInfo, &alloc_callbacks, &device);
            ASSERT_EQ(result, VK_SUCCESS);

            alloc_callbacks.pUserData = (void *)0x00000033;
            vkDestroyDevice(device, &alloc_callbacks);
        }
    }

    alloc_callbacks.pUserData = (void *)0x00000034;
    vkDestroyInstance(instance, &alloc_callbacks);

    // Make sure everything's been freed
    ASSERT_EQ(true, IsAllocTrackerEmpty());
    FreeAllocTracker();
}

// Test making sure the allocation functions are called to allocate and cleanup everything from
// vkCreateInstance, to vkCreateDevicce, and then through their destructors.  With special
// allocators used on only the instance and not the device.
TEST(Allocation, InstanceButNotDevice) {
    auto const info = VK::InstanceCreateInfo();
    VkInstance instance = VK_NULL_HANDLE;
    VkAllocationCallbacks alloc_callbacks = {};
    alloc_callbacks.pUserData = (void *)0x00000041;
    alloc_callbacks.pfnAllocation = AllocCallbackFunc;
    alloc_callbacks.pfnReallocation = ReallocCallbackFunc;
    alloc_callbacks.pfnFree = FreeCallbackFunc;

    InitAllocTracker(2048);

    VkResult result = vkCreateInstance(info, &alloc_callbacks, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    for (uint32_t p = 0; p < physicalCount; ++p) {
        uint32_t familyCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, nullptr);
        ASSERT_GT(familyCount, 0u);

        std::unique_ptr<VkQueueFamilyProperties[]> family(new VkQueueFamilyProperties[familyCount]);
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, family.get());
        ASSERT_GT(familyCount, 0u);

        for (uint32_t q = 0; q < familyCount; ++q) {
            if (~family[q].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                continue;
            }

            float const priorities[] = {0.0f};  // Temporary required due to MSVC bug.
            VkDeviceQueueCreateInfo const queueInfo[1]{
                VK::DeviceQueueCreateInfo().queueFamilyIndex(q).queueCount(1).pQueuePriorities(priorities)};

            auto const deviceInfo = VK::DeviceCreateInfo().queueCreateInfoCount(1).pQueueCreateInfos(queueInfo);

            VkDevice device;
            result = vkCreateDevice(physical[p], deviceInfo, NULL, &device);
            ASSERT_EQ(result, VK_SUCCESS);

            vkDestroyDevice(device, NULL);
        }
    }

    alloc_callbacks.pUserData = (void *)0x00000042;
    vkDestroyInstance(instance, &alloc_callbacks);

    // Make sure everything's been freed
    ASSERT_EQ(true, IsAllocTrackerEmpty());
    FreeAllocTracker();
}

// Test making sure the allocation functions are called to allocate and cleanup everything from
// vkCreateInstance, to vkCreateDevicce, and then through their destructors.  With special
// allocators used on only the device and not the instance.
TEST(Allocation, DeviceButNotInstance) {
    auto const info = VK::InstanceCreateInfo();
    VkInstance instance = VK_NULL_HANDLE;
    VkAllocationCallbacks alloc_callbacks = {};
    alloc_callbacks.pfnAllocation = AllocCallbackFunc;
    alloc_callbacks.pfnReallocation = ReallocCallbackFunc;
    alloc_callbacks.pfnFree = FreeCallbackFunc;

    InitAllocTracker(2048);

    VkResult result = vkCreateInstance(info, NULL, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    for (uint32_t p = 0; p < physicalCount; ++p) {
        uint32_t familyCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, nullptr);
        ASSERT_GT(familyCount, 0u);

        std::unique_ptr<VkQueueFamilyProperties[]> family(new VkQueueFamilyProperties[familyCount]);
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, family.get());
        ASSERT_GT(familyCount, 0u);

        for (uint32_t q = 0; q < familyCount; ++q) {
            if (~family[q].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                continue;
            }

            float const priorities[] = {0.0f};  // Temporary required due to MSVC bug.
            VkDeviceQueueCreateInfo const queueInfo[1]{
                VK::DeviceQueueCreateInfo().queueFamilyIndex(q).queueCount(1).pQueuePriorities(priorities)};

            auto const deviceInfo = VK::DeviceCreateInfo().queueCreateInfoCount(1).pQueueCreateInfos(queueInfo);

            VkDevice device;
            alloc_callbacks.pUserData = (void *)0x00000051;
            result = vkCreateDevice(physical[p], deviceInfo, &alloc_callbacks, &device);
            ASSERT_EQ(result, VK_SUCCESS);

            alloc_callbacks.pUserData = (void *)0x00000052;
            vkDestroyDevice(device, &alloc_callbacks);
        }
    }

    vkDestroyInstance(instance, NULL);

    // Make sure everything's been freed
    ASSERT_EQ(true, IsAllocTrackerEmpty());
    FreeAllocTracker();
}

// Test failure during vkCreateInstance to make sure we don't leak memory if
// one of the out-of-memory conditions trigger.
TEST(Allocation, CreateInstanceIntentionalAllocFail) {
    auto const info = VK::InstanceCreateInfo();
    VkInstance instance = VK_NULL_HANDLE;
    VkAllocationCallbacks alloc_callbacks = {};
    alloc_callbacks.pfnAllocation = AllocCallbackFunc;
    alloc_callbacks.pfnReallocation = ReallocCallbackFunc;
    alloc_callbacks.pfnFree = FreeCallbackFunc;

    VkResult result;
    uint32_t fail_index = 1;
    do {
        InitAllocTracker(9999, fail_index);

        result = vkCreateInstance(info, &alloc_callbacks, &instance);
        if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
            if (!IsAllocTrackerEmpty()) {
                std::cout << "Failed on index " << fail_index << '\n';
                ASSERT_EQ(true, IsAllocTrackerEmpty());
            }
        }
        fail_index++;
        // Make sure we don't overrun the memory
        ASSERT_LT(fail_index, 9999u);

        FreeAllocTracker();
    } while (result == VK_ERROR_OUT_OF_HOST_MEMORY);

    vkDestroyInstance(instance, &alloc_callbacks);
}

// Test failure during vkCreateDevice to make sure we don't leak memory if
// one of the out-of-memory conditions trigger.
TEST(Allocation, CreateDeviceIntentionalAllocFail) {
    auto const info = VK::InstanceCreateInfo();
    VkInstance instance = VK_NULL_HANDLE;
    VkDevice device = VK_NULL_HANDLE;
    VkAllocationCallbacks alloc_callbacks = {};
    alloc_callbacks.pfnAllocation = AllocCallbackFunc;
    alloc_callbacks.pfnReallocation = ReallocCallbackFunc;
    alloc_callbacks.pfnFree = FreeCallbackFunc;

    VkResult result = vkCreateInstance(info, NULL, &instance);
    ASSERT_EQ(result, VK_SUCCESS);

    uint32_t physicalCount = 0;
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
    result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(physicalCount, 0u);

    for (uint32_t p = 0; p < physicalCount; ++p) {
        uint32_t familyCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, nullptr);
        ASSERT_GT(familyCount, 0u);

        std::unique_ptr<VkQueueFamilyProperties[]> family(new VkQueueFamilyProperties[familyCount]);
        vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &familyCount, family.get());
        ASSERT_GT(familyCount, 0u);

        for (uint32_t q = 0; q < familyCount; ++q) {
            if (~family[q].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                continue;
            }

            float const priorities[] = {0.0f};  // Temporary required due to MSVC bug.
            VkDeviceQueueCreateInfo const queueInfo[1]{
                VK::DeviceQueueCreateInfo().queueFamilyIndex(q).queueCount(1).pQueuePriorities(priorities)};

            auto const deviceInfo = VK::DeviceCreateInfo().queueCreateInfoCount(1).pQueueCreateInfos(queueInfo);

            uint32_t fail_index = 1;
            do {
                InitAllocTracker(9999, fail_index);

                result = vkCreateDevice(physical[p], deviceInfo, &alloc_callbacks, &device);
                if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
                    if (!IsAllocTrackerEmpty()) {
                        std::cout << "Failed on index " << fail_index << '\n';
                        ASSERT_EQ(true, IsAllocTrackerEmpty());
                    }
                }
                fail_index++;
                // Make sure we don't overrun the memory
                ASSERT_LT(fail_index, 9999u);

                FreeAllocTracker();
            } while (result == VK_ERROR_OUT_OF_HOST_MEMORY);
            vkDestroyDevice(device, &alloc_callbacks);
            break;
        }
    }

    vkDestroyInstance(instance, NULL);
}

// Test failure during vkCreateInstance and vkCreateDevice to make sure we don't
// leak memory if one of the out-of-memory conditions trigger.
TEST(Allocation, CreateInstanceDeviceIntentionalAllocFail) {
    auto const info = VK::InstanceCreateInfo();
    VkInstance instance = VK_NULL_HANDLE;
    VkDevice device = VK_NULL_HANDLE;
    VkAllocationCallbacks alloc_callbacks = {};
    alloc_callbacks.pfnAllocation = AllocCallbackFunc;
    alloc_callbacks.pfnReallocation = ReallocCallbackFunc;
    alloc_callbacks.pfnFree = FreeCallbackFunc;

    VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
    uint32_t fail_index = 0;
    uint32_t physicalCount = 0;
    while (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
        InitAllocTracker(9999, ++fail_index);
        ASSERT_LT(fail_index, 9999u);

        result = vkCreateInstance(info, &alloc_callbacks, &instance);
        if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
            if (!IsAllocTrackerEmpty()) {
                std::cout << "Failed on index " << fail_index << '\n';
                ASSERT_EQ(true, IsAllocTrackerEmpty());
            }
            FreeAllocTracker();
            continue;
        }
        ASSERT_EQ(result, VK_SUCCESS);

        physicalCount = 0;
        result = vkEnumeratePhysicalDevices(instance, &physicalCount, nullptr);
        if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
            vkDestroyInstance(instance, &alloc_callbacks);
            if (!IsAllocTrackerEmpty()) {
                std::cout << "Failed on index " << fail_index << '\n';
                ASSERT_EQ(true, IsAllocTrackerEmpty());
            }
            FreeAllocTracker();
            continue;
        }
        ASSERT_EQ(result, VK_SUCCESS);

        std::unique_ptr<VkPhysicalDevice[]> physical(new VkPhysicalDevice[physicalCount]);
        result = vkEnumeratePhysicalDevices(instance, &physicalCount, physical.get());
        if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
            vkDestroyInstance(instance, &alloc_callbacks);
            if (!IsAllocTrackerEmpty()) {
                std::cout << "Failed on index " << fail_index << '\n';
                ASSERT_EQ(true, IsAllocTrackerEmpty());
            }
            FreeAllocTracker();
            continue;
        }
        ASSERT_EQ(result, VK_SUCCESS);

        uint32_t familyCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(physical[0], &familyCount, nullptr);
        ASSERT_GT(familyCount, 0u);

        std::unique_ptr<VkQueueFamilyProperties[]> family(new VkQueueFamilyProperties[familyCount]);
        vkGetPhysicalDeviceQueueFamilyProperties(physical[0], &familyCount, family.get());
        ASSERT_GT(familyCount, 0u);

        uint32_t queue_index = 0;
        for (uint32_t q = 0; q < familyCount; ++q) {
            if (family[q].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                queue_index = q;
                break;
            }
        }

        float const priorities[] = {0.0f};  // Temporary required due to MSVC bug.
        VkDeviceQueueCreateInfo const queueInfo[1]{
            VK::DeviceQueueCreateInfo().queueFamilyIndex(queue_index).queueCount(1).pQueuePriorities(priorities)};

        auto const deviceInfo = VK::DeviceCreateInfo().queueCreateInfoCount(1).pQueueCreateInfos(queueInfo);

        result = vkCreateDevice(physical[0], deviceInfo, &alloc_callbacks, &device);
        if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
            vkDestroyInstance(instance, &alloc_callbacks);
            if (!IsAllocTrackerEmpty()) {
                std::cout << "Failed on index " << fail_index << '\n';
                ASSERT_EQ(true, IsAllocTrackerEmpty());
            }
            FreeAllocTracker();
            continue;
        }
        vkDestroyDevice(device, &alloc_callbacks);
        vkDestroyInstance(instance, &alloc_callbacks);
        FreeAllocTracker();
    }
}

// Used by run_loader_tests.sh to test that calling vkEnumeratePhysicalDeviceGroupsKHX without first querying
// the count, works.  And, that it also returns only physical devices made available by the standard
// enumerate call
TEST(EnumeratePhysicalDeviceGroupsKHX, OneCall) {
    VkInstance instance = VK_NULL_HANDLE;
    char const *const names[] = {VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME};
    auto const info = VK::InstanceCreateInfo().enabledExtensionCount(1).ppEnabledExtensionNames(names);
    uint32_t group;
    uint32_t dev;
    std::vector<std::pair<VkPhysicalDevice, bool>> phys_dev_normal_found;
    std::vector<std::pair<VkPhysicalDevice, bool>> phys_dev_group_found;

    VkResult result = vkCreateInstance(info, VK_NULL_HANDLE, &instance);
    if (result == VK_ERROR_EXTENSION_NOT_PRESENT) {
        // Extension isn't present, just skip this test
        ASSERT_EQ(result, VK_ERROR_EXTENSION_NOT_PRESENT);
        std::cout << "Skipping EnumeratePhysicalDeviceGroupsKHX : OneCall due to Instance lacking support"
                  << " for " << VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME << " extension\n";
        return;
    }

    uint32_t phys_dev_count = 500;
    std::unique_ptr<VkPhysicalDevice[]> phys_devs(new VkPhysicalDevice[phys_dev_count]);
    result = vkEnumeratePhysicalDevices(instance, &phys_dev_count, phys_devs.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(phys_dev_count, 0u);

    // Initialize the normal physical device boolean pair array
    for (dev = 0; dev < phys_dev_count; dev++) {
        phys_dev_normal_found.push_back(std::make_pair(phys_devs[dev], false));
    }

    // Get a pointer to the new vkEnumeratePhysicalDeviceGroupsKHX call
    PFN_vkEnumeratePhysicalDeviceGroupsKHX p_vkEnumeratePhysicalDeviceGroupsKHX =
        (PFN_vkEnumeratePhysicalDeviceGroupsKHX)vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDeviceGroupsKHX");

    // Setup the group information in preparation for the call
    uint32_t group_count = 30;
    std::unique_ptr<VkPhysicalDeviceGroupPropertiesKHX[]> phys_dev_groups(new VkPhysicalDeviceGroupPropertiesKHX[group_count]);
    for (group = 0; group < group_count; group++) {
        phys_dev_groups[group].sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHX;
        phys_dev_groups[group].pNext = nullptr;
        phys_dev_groups[group].physicalDeviceCount = 0;
        memset(phys_dev_groups[group].physicalDevices, 0, sizeof(VkPhysicalDevice) * VK_MAX_DEVICE_GROUP_SIZE_KHX);
        phys_dev_groups[group].subsetAllocation = VK_FALSE;
    }

    result = p_vkEnumeratePhysicalDeviceGroupsKHX(instance, &group_count, phys_dev_groups.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(group_count, 0u);

    // Initialize the group physical device boolean pair array
    for (group = 0; group < group_count; group++) {
        for (dev = 0; dev < phys_dev_groups[group].physicalDeviceCount; dev++) {
            phys_dev_group_found.push_back(std::make_pair(phys_dev_groups[group].physicalDevices[dev], false));
        }
    }

    // Now, make sure we can find each normal and group item in the other list
    for (dev = 0; dev < phys_dev_count; dev++) {
        for (group = 0; group < phys_dev_group_found.size(); group++) {
            if (phys_dev_normal_found[dev].first == phys_dev_group_found[group].first) {
                phys_dev_normal_found[dev].second = true;
                phys_dev_group_found[group].second = true;
                break;
            }
        }
    }

    for (dev = 0; dev < phys_dev_count; dev++) {
        ASSERT_EQ(phys_dev_normal_found[dev].second, true);
    }
    for (dev = 0; dev < phys_dev_group_found.size(); dev++) {
        ASSERT_EQ(phys_dev_group_found[dev].second, true);
    }

    vkDestroyInstance(instance, nullptr);
}

// Used by run_loader_tests.sh to test for the expected usage of the
// vkEnumeratePhysicalDeviceGroupsKHX call in a two call fasion (once with NULL data
// to get count, and then again with data).
TEST(EnumeratePhysicalDeviceGroupsKHX, TwoCall) {
    VkInstance instance = VK_NULL_HANDLE;
    char const *const names[] = {VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME};
    auto const info = VK::InstanceCreateInfo().enabledExtensionCount(1).ppEnabledExtensionNames(names);
    uint32_t group;
    uint32_t group_count;
    uint32_t dev;
    std::vector<std::pair<VkPhysicalDevice, bool>> phys_dev_normal_found;
    std::vector<std::pair<VkPhysicalDevice, bool>> phys_dev_group_found;

    VkResult result = vkCreateInstance(info, VK_NULL_HANDLE, &instance);
    if (result == VK_ERROR_EXTENSION_NOT_PRESENT) {
        // Extension isn't present, just skip this test
        ASSERT_EQ(result, VK_ERROR_EXTENSION_NOT_PRESENT);
        std::cout << "Skipping EnumeratePhysicalDeviceGroupsKHX : TwoCall due to Instance lacking support"
                  << " for " << VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME << " extension\n";
        return;
    }

    // Get a pointer to the new vkEnumeratePhysicalDeviceGroupsKHX call
    PFN_vkEnumeratePhysicalDeviceGroupsKHX p_vkEnumeratePhysicalDeviceGroupsKHX =
        (PFN_vkEnumeratePhysicalDeviceGroupsKHX)vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDeviceGroupsKHX");

    // Setup the group information in preparation for the call
    uint32_t array_group_count = 30;
    std::unique_ptr<VkPhysicalDeviceGroupPropertiesKHX[]> phys_dev_groups(
        new VkPhysicalDeviceGroupPropertiesKHX[array_group_count]);
    for (group = 0; group < array_group_count; group++) {
        phys_dev_groups[group].sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHX;
        phys_dev_groups[group].pNext = nullptr;
        phys_dev_groups[group].physicalDeviceCount = 0;
        memset(phys_dev_groups[group].physicalDevices, 0, sizeof(VkPhysicalDevice) * VK_MAX_DEVICE_GROUP_SIZE_KHX);
        phys_dev_groups[group].subsetAllocation = VK_FALSE;
    }

    result = p_vkEnumeratePhysicalDeviceGroupsKHX(instance, &group_count, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(group_count, 0u);
    ASSERT_LT(group_count, array_group_count);

    result = p_vkEnumeratePhysicalDeviceGroupsKHX(instance, &group_count, phys_dev_groups.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(group_count, 0u);
    ASSERT_LT(group_count, array_group_count);

    // Initialize the group physical device boolean pair array
    for (group = 0; group < group_count; group++) {
        for (dev = 0; dev < phys_dev_groups[group].physicalDeviceCount; dev++) {
            phys_dev_group_found.push_back(std::make_pair(phys_dev_groups[group].physicalDevices[dev], false));
        }
    }

    uint32_t phys_dev_count = 500;
    std::unique_ptr<VkPhysicalDevice[]> phys_devs(new VkPhysicalDevice[phys_dev_count]);
    result = vkEnumeratePhysicalDevices(instance, &phys_dev_count, phys_devs.get());
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(phys_dev_count, 0u);

    // Initialize the normal physical device boolean pair array
    for (dev = 0; dev < phys_dev_count; dev++) {
        phys_dev_normal_found.push_back(std::make_pair(phys_devs[dev], false));
    }

    // Now, make sure we can find each normal and group item in the other list
    for (dev = 0; dev < phys_dev_count; dev++) {
        for (group = 0; group < phys_dev_group_found.size(); group++) {
            if (phys_dev_normal_found[dev].first == phys_dev_group_found[group].first) {
                phys_dev_normal_found[dev].second = true;
                phys_dev_group_found[group].second = true;
                break;
            }
        }
    }

    for (dev = 0; dev < phys_dev_count; dev++) {
        ASSERT_EQ(phys_dev_normal_found[dev].second, true);
    }
    for (dev = 0; dev < phys_dev_group_found.size(); dev++) {
        ASSERT_EQ(phys_dev_group_found[dev].second, true);
    }

    vkDestroyInstance(instance, nullptr);
}

// Used by run_loader_tests.sh to test for the expected usage of the EnumeratePhysicalDeviceGroupsKHX
// call if not enough numbers are provided for the final list.
TEST(EnumeratePhysicalDeviceGroupsKHX, TwoCallIncomplete) {
    VkInstance instance = VK_NULL_HANDLE;
    char const *const names[] = {VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME};
    auto const info = VK::InstanceCreateInfo().enabledExtensionCount(1).ppEnabledExtensionNames(names);
    uint32_t group;
    uint32_t group_count;

    VkResult result = vkCreateInstance(info, VK_NULL_HANDLE, &instance);
    if (result == VK_ERROR_EXTENSION_NOT_PRESENT) {
        // Extension isn't present, just skip this test
        ASSERT_EQ(result, VK_ERROR_EXTENSION_NOT_PRESENT);
        std::cout << "Skipping EnumeratePhysicalDeviceGroupsKHX : TwoCallIncomplete due to Instance lacking support"
                  << " for " << VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME << " extension\n";
        return;
    }

    // Get a pointer to the new vkEnumeratePhysicalDeviceGroupsKHX call
    PFN_vkEnumeratePhysicalDeviceGroupsKHX p_vkEnumeratePhysicalDeviceGroupsKHX =
        (PFN_vkEnumeratePhysicalDeviceGroupsKHX)vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDeviceGroupsKHX");

    // Setup the group information in preparation for the call
    uint32_t array_group_count = 30;
    std::unique_ptr<VkPhysicalDeviceGroupPropertiesKHX[]> phys_dev_groups(
        new VkPhysicalDeviceGroupPropertiesKHX[array_group_count]);
    for (group = 0; group < array_group_count; group++) {
        phys_dev_groups[group].sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHX;
        phys_dev_groups[group].pNext = nullptr;
        phys_dev_groups[group].physicalDeviceCount = 0;
        memset(phys_dev_groups[group].physicalDevices, 0, sizeof(VkPhysicalDevice) * VK_MAX_DEVICE_GROUP_SIZE_KHX);
        phys_dev_groups[group].subsetAllocation = VK_FALSE;
    }

    result = p_vkEnumeratePhysicalDeviceGroupsKHX(instance, &group_count, nullptr);
    ASSERT_EQ(result, VK_SUCCESS);
    ASSERT_GT(group_count, 0u);
    ASSERT_LT(group_count, array_group_count);

    group_count -= 1;

    result = p_vkEnumeratePhysicalDeviceGroupsKHX(instance, &group_count, phys_dev_groups.get());
    ASSERT_EQ(result, VK_INCOMPLETE);

    vkDestroyInstance(instance, nullptr);
}

int main(int argc, char **argv) {
    int result;

    ::testing::InitGoogleTest(&argc, argv);

    if (argc > 0) {
        CommandLine::Initialize(argc, argv);
    }

    result = RUN_ALL_TESTS();

    return result;
}