tests: Add TestDestroyFreeNullHandles test

NOTE: To pass this test will require a driver that is tolerant
      of NULL handles, probably the latest available

Add test that calls all applicable destroy and free routines with NULL
handles, expecting no validation errors

Change-Id: If218748933105b7ba17534e41436d29a19effc25
diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index ab1c4f3..0d2547c 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -17412,6 +17412,102 @@
     vkDestroyImage(m_device->device(), image, NULL);
 }
 
+// This is a positive test. No failures are expected.
+TEST_F(VkPositiveLayerTest, TestDestroyFreeNullHandles) {
+    VkResult err;
+
+    TEST_DESCRIPTION(
+        "Call all applicable destroy and free routines with NULL"
+        "handles, expecting no validation errors");
+
+    m_errorMonitor->ExpectSuccess();
+
+    ASSERT_NO_FATAL_FAILURE(InitState());
+    vkDestroyBuffer(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyBufferView(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyCommandPool(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyDescriptorPool(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyDescriptorSetLayout(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyDevice(VK_NULL_HANDLE, NULL);
+    vkDestroyEvent(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyFence(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyFramebuffer(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyImage(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyImageView(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyInstance(VK_NULL_HANDLE, NULL);
+    vkDestroyPipeline(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyPipelineCache(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyPipelineLayout(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyQueryPool(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyRenderPass(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroySampler(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroySemaphore(m_device->device(), VK_NULL_HANDLE, NULL);
+    vkDestroyShaderModule(m_device->device(), VK_NULL_HANDLE, NULL);
+
+    VkCommandPool command_pool;
+    VkCommandPoolCreateInfo pool_create_info{};
+    pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+    pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
+    pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+    vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
+    VkCommandBuffer command_buffers[3] = {};
+    VkCommandBufferAllocateInfo command_buffer_allocate_info{};
+    command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+    command_buffer_allocate_info.commandPool = command_pool;
+    command_buffer_allocate_info.commandBufferCount = 1;
+    command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+    vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, &command_buffers[1]);
+    vkFreeCommandBuffers(m_device->device(), command_pool, 3, command_buffers);
+    vkDestroyCommandPool(m_device->device(), command_pool, NULL);
+
+    VkDescriptorPoolSize ds_type_count = {};
+    ds_type_count.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+    ds_type_count.descriptorCount = 1;
+
+    VkDescriptorPoolCreateInfo ds_pool_ci = {};
+    ds_pool_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+    ds_pool_ci.pNext = NULL;
+    ds_pool_ci.maxSets = 1;
+    ds_pool_ci.poolSizeCount = 1;
+    ds_pool_ci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
+    ds_pool_ci.pPoolSizes = &ds_type_count;
+
+    VkDescriptorPool ds_pool;
+    err = vkCreateDescriptorPool(m_device->device(), &ds_pool_ci, NULL, &ds_pool);
+    ASSERT_VK_SUCCESS(err);
+
+    VkDescriptorSetLayoutBinding dsl_binding = {};
+    dsl_binding.binding = 2;
+    dsl_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+    dsl_binding.descriptorCount = 1;
+    dsl_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+    dsl_binding.pImmutableSamplers = NULL;
+    VkDescriptorSetLayoutCreateInfo ds_layout_ci = {};
+    ds_layout_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+    ds_layout_ci.pNext = NULL;
+    ds_layout_ci.bindingCount = 1;
+    ds_layout_ci.pBindings = &dsl_binding;
+    VkDescriptorSetLayout ds_layout;
+    err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
+    ASSERT_VK_SUCCESS(err);
+
+    VkDescriptorSet descriptor_sets[3] = {};
+    VkDescriptorSetAllocateInfo alloc_info = {};
+    alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+    alloc_info.descriptorSetCount = 1;
+    alloc_info.descriptorPool = ds_pool;
+    alloc_info.pSetLayouts = &ds_layout;
+    err = vkAllocateDescriptorSets(m_device->device(), &alloc_info, &descriptor_sets[1]);
+    ASSERT_VK_SUCCESS(err);
+    vkFreeDescriptorSets(m_device->device(), ds_pool, 3, descriptor_sets);
+    vkDestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
+    vkDestroyDescriptorPool(m_device->device(), ds_pool, NULL);
+
+    vkFreeMemory(m_device->device(), VK_NULL_HANDLE, NULL);
+
+    m_errorMonitor->VerifyNotFound();
+}
+
 TEST_F(VkPositiveLayerTest, DynamicOffsetWithInactiveBinding) {
     // Create a descriptorSet w/ dynamic descriptors where 1 binding is inactive
     // We previously had a bug where dynamic offset of inactive bindings was still being used