/*
 * Copyright (c) 2023 The Khronos Group Inc.
 * Copyright (c) 2023 Valve Corporation
 * Copyright (c) 2023 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: Charles Giessen <charles@lunarg.com>
 */

#include "test_environment.h"

#include <fstream>

#include "util/get_executable_path.h"
#include "util/json_writer.h"
#include "util/test_defines.h"

std::string get_settings_location_log_message([[maybe_unused]] FrameworkEnvironment const& env, bool use_secure = true) {
    std::string s = "Using layer configurations found in loader settings from ";
#if defined(WIN32)
    ManifestLocation settings_location = use_secure ? ManifestLocation::settings_location : ManifestLocation::unsecured_settings;
    return s + (env.get_folder(settings_location).location() / "vk_loader_settings.json").string();
#elif TESTING_COMMON_UNIX_PLATFORMS
    return s + (use_secure ? env.secure_manifest_base_location : env.unsecure_manifest_base_location) +
           "/" TESTING_VULKAN_DIR "/loader_settings.d/vk_loader_settings.json";
#endif
}
std::string get_unsecure_settings_location_log_message(FrameworkEnvironment const& env) {
    return get_settings_location_log_message(env, false);
}

std::string get_settings_not_in_use_log_message([[maybe_unused]] FrameworkEnvironment const& env, bool use_secure) {
    std::string s = "vk_loader_settings.json file found at \"";
#if defined(WIN32)
    ManifestLocation settings_location = use_secure ? ManifestLocation::settings_location : ManifestLocation::unsecured_settings;
    return s + (env.get_folder(settings_location).location() / "vk_loader_settings.json").string();
#elif TESTING_COMMON_UNIX_PLATFORMS
    return s + (use_secure ? env.secure_manifest_base_location : env.unsecure_manifest_base_location) +
           "/" TESTING_VULKAN_DIR "/loader_settings.d/vk_loader_settings.json\" but did not contain any valid settings.";
#endif
}
enum class LayerType {
    exp,
    imp,
    imp_with_enable_env,
};
const char* add_layer_and_settings(FrameworkEnvironment& env, const char* layer_name, LayerType layer_type, const char* control) {
    if (layer_type == LayerType::imp) {
        env.add_implicit_layer(
            ManifestOptions{}.set_json_name(std::string(layer_name) + std::to_string(env.layers.size()) + ".json"),
            ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                          .set_name(layer_name)
                                          .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                          .set_disable_environment("BADGER" + std::to_string(env.layers.size()))));
    } else if (layer_type == LayerType::imp_with_enable_env) {
        env.add_implicit_layer(
            ManifestOptions{}.set_json_name(std::string(layer_name) + std::to_string(env.layers.size()) + ".json"),
            ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                          .set_name(layer_name)
                                          .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                          .set_disable_environment("BADGER" + std::to_string(env.layers.size()))
                                          .set_enable_environment("MUSHROOM" + std::to_string(env.layers.size()))));
    } else if (layer_type == LayerType::exp) {
        env.add_explicit_layer(
            ManifestOptions{}.set_json_name(std::string(layer_name) + std::to_string(env.layers.size()) + ".json"),
            ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));
    } else {
        abort();
    }
    env.loader_settings.app_specific_settings.back().add_layer_configuration(
        LoaderSettingsLayerConfiguration{}
            .set_name(layer_name)
            .set_control(control)
            .set_treat_as_implicit_manifest(layer_type != LayerType::exp)
            .set_path(env.get_shimmed_layer_manifest_path(env.layers.size() - 1)));
    return layer_name;
}

// Make sure settings layer is found and that a layer defined in it is loaded
TEST(SettingsFile, FileExist) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all"));
    const char* regular_layer_name = add_layer_and_settings(env, "VK_LAYER_TestLayer_0", LayerType::exp, "on");
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(1);
        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();

        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, regular_layer_name));
    }
}

// Make sure that if the settings file is in a user local path, that it isn't used when running with elevated privileges
TEST(SettingsFile, SettingsInUnsecuredLocation) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    const char* regular_layer_name = "VK_LAYER_TestLayer_0";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));
    env.update_loader_settings(
        env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
            AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                                                           .set_name(regular_layer_name)
                                                                                           .set_path(env.get_layer_manifest_path())
                                                                                           .set_control("on"))),
        false);

    auto layer_props = env.GetLayerProperties(1);
    EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name));

    InstWrapper inst{env.vulkan_functions};
    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
    inst.CheckCreate();

    ASSERT_TRUE(env.debug_log.find(get_unsecure_settings_location_log_message(env)));
    env.debug_log.clear();
    auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
    ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name));
}

TEST(SettingsFile, SettingsInUnsecuredLocationRunningWithElevatedPrivileges) {
    FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    const char* regular_layer_name = "VK_LAYER_TestLayer_0";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));
    env.update_loader_settings(
        env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
            AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                                                           .set_name(regular_layer_name)
                                                                                           .set_path(env.get_layer_manifest_path())
                                                                                           .set_control("on"))),
        false);

    ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));

    InstWrapper inst{env.vulkan_functions};
    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
    inst.CheckCreate();

    ASSERT_FALSE(env.debug_log.find(get_unsecure_settings_location_log_message(env)));
    ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
}

TEST(SettingsFile, SettingsInSecuredLocation) {
    FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    env.add_icd(TEST_ICD_PATH_VERSION_2);
    const char* regular_layer_name = "VK_LAYER_TestLayer_0";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));
    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                                                       .set_name(regular_layer_name)
                                                                                       .set_path(env.get_layer_manifest_path())
                                                                                       .set_control("on"))));

    auto layer_props = env.GetLayerProperties(1);
    EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name));

    InstWrapper inst{env.vulkan_functions};
    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
    inst.CheckCreate();

    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
    env.debug_log.clear();
    auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
    ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name));
}

// Make sure settings file can have multiple sets of settings
TEST(SettingsFile, SupportsMultipleSettingsSimultaneously) {
    FrameworkEnvironment env{};
    const char* app_specific_layer_name = "VK_LAYER_TestLayer_0";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(app_specific_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* global_layer_name = "VK_LAYER_TestLayer_1";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(global_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.update_loader_settings(
        env.loader_settings
            // configuration that matches the current executable path - but dont set the app-key just yet
            .add_app_specific_setting(AppSpecificSettings{}
                                          .add_stderr_log_filter("all")
                                          .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                                       .set_name(app_specific_layer_name)
                                                                       .set_path(env.get_layer_manifest_path(0))
                                                                       .set_control("on"))
                                          .add_app_key("key0"))
            // configuration that should never be used
            .add_app_specific_setting(
                AppSpecificSettings{}
                    .add_stderr_log_filter("all")
                    .add_layer_configuration(
                        LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_haha").set_path("/made/up/path").set_control("auto"))
                    .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                 .set_name("VK_LAYER_haha2")
                                                 .set_path("/made/up/path2")
                                                 .set_control("auto"))
                    .add_app_key("key1")
                    .add_app_key("key2"))
            // Add a global configuration
            .add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
                LoaderSettingsLayerConfiguration{}
                    .set_name(global_layer_name)
                    .set_path(env.get_layer_manifest_path(1))
                    .set_control("on"))));
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    {
        auto layer_props = env.GetLayerProperties(1);
        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, global_layer_name));

        // Check that the global config is used
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, global_layer_name));
    }
    env.debug_log.clear();
    // Set one set to contain the current executable path
    env.loader_settings.app_specific_settings.at(0).add_app_key(test_platform_executable_path());
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(1);
        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, app_specific_layer_name));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, app_specific_layer_name));
    }
}

// Make sure layers found through the settings file are enableable by environment variables
TEST(SettingsFile, LayerAutoEnabledByEnvVars) {
    FrameworkEnvironment env{};
    env.loader_settings.set_file_format_version({1, 0, 0});
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* layer_name = "VK_LAYER_automatic";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.update_loader_settings(
        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
            LoaderSettingsLayerConfiguration{}.set_name(layer_name).set_path(env.get_layer_manifest_path(0)).set_control("auto"))));
    env.add_icd(TEST_ICD_PATH_VERSION_2);
    {
        EnvVarWrapper instance_layers{"VK_INSTANCE_LAYERS", layer_name};
        auto layer_props = env.GetLayerProperties(1);
        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, layer_name));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_name));
    }
    env.debug_log.clear();

    {
        EnvVarWrapper loader_layers_enable{"VK_LOADER_LAYERS_ENABLE", layer_name};
        auto layer_props = env.GetLayerProperties(1);
        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, layer_name));
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_name));
    }
}

// Make sure layers are disallowed from loading if the settings file says so
TEST(SettingsFile, LayerDisablesImplicitLayer) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    const char* implicit_layer_name = "VK_LAYER_Implicit_TestLayer";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("oof")));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
            LoaderSettingsLayerConfiguration{}
                .set_name(implicit_layer_name)
                .set_path(env.get_shimmed_layer_manifest_path(0))
                .set_control("off")
                .set_treat_as_implicit_manifest(true))));
    {
        ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();

        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
    }
}

// Implicit layers should be reordered by the settings file
TEST(SettingsFile, ImplicitLayersDontInterfere) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name1)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("oof")));
    const char* implicit_layer_name2 = "VK_LAYER_Implicit_TestLayer2";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name2)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("oof")));
    // validate order when settings file is not present
    {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name2));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name2));
    }
    // Now setup the settings file to contain a specific order
    env.update_loader_settings(
        LoaderSettings{}.add_app_specific_setting(AppSpecificSettings{}
                                                      .add_stderr_log_filter("all")
                                                      .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                                                   .set_name(implicit_layer_name1)
                                                                                   .set_path(env.get_shimmed_layer_manifest_path(0))
                                                                                   .set_control("auto")
                                                                                   .set_treat_as_implicit_manifest(true))
                                                      .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                                                   .set_name(implicit_layer_name2)
                                                                                   .set_path(env.get_shimmed_layer_manifest_path(1))
                                                                                   .set_control("auto")
                                                                                   .set_treat_as_implicit_manifest(true))));
    {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name2));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name2));
    }

    // Flip the order and store the settings in the env for later use in the test
    env.loader_settings =
        LoaderSettings{}.add_app_specific_setting(AppSpecificSettings{}
                                                      .add_stderr_log_filter("all")
                                                      .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                                                   .set_name(implicit_layer_name2)
                                                                                   .set_path(env.get_shimmed_layer_manifest_path(1))
                                                                                   .set_control("auto")
                                                                                   .set_treat_as_implicit_manifest(true))
                                                      .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                                                   .set_name(implicit_layer_name1)
                                                                                   .set_path(env.get_shimmed_layer_manifest_path(0))
                                                                                   .set_control("auto")
                                                                                   .set_treat_as_implicit_manifest(true)));
    env.update_loader_settings(env.loader_settings);

    {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name1));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name2));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name1));
    }

    // Now add an explicit layer into the middle and verify that is in the correct location
    const char* explicit_layer_name3 = "VK_LAYER_Explicit_TestLayer3";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name3).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.loader_settings.app_specific_settings.at(0).layer_configurations.insert(
        env.loader_settings.app_specific_settings.at(0).layer_configurations.begin() + 1,
        LoaderSettingsLayerConfiguration{}
            .set_name(explicit_layer_name3)
            .set_path(env.get_shimmed_layer_manifest_path(2))
            .set_control("on"));
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(3);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, implicit_layer_name1));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name2));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name3));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, implicit_layer_name1));
    }
}

// Make sure layers that are disabled can't be enabled by the application
TEST(SettingsFile, ApplicationEnablesIgnored) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    const char* explicit_layer_name = "VK_LAYER_TestLayer";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
            LoaderSettingsLayerConfiguration{}
                .set_name(explicit_layer_name)
                .set_path(env.get_shimmed_layer_manifest_path(0))
                .set_control("off"))));
    {
        ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));
        InstWrapper inst{env.vulkan_functions};
        inst.create_info.add_layer(explicit_layer_name);
        ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT));
    }
}

// If the layer list is empty, we want no layers found or loaded
TEST(SettingsFile, LayerListIsEmpty) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    const char* implicit_layer_name = "VK_LAYER_TestLayer";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("HeeHee")));

    JsonWriter writer{};
    writer.StartObject();
    writer.AddKeyedString("file_format_version", "1.0.0");
    writer.StartKeyedObject("settings");
    writer.StartKeyedArray("layers");
    writer.EndArray();
    writer.EndObject();
    writer.EndObject();
    env.write_settings_file(writer.output, true);

    ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));

    InstWrapper inst{env.vulkan_functions};
    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
    inst.CheckCreate();
    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env, true)));
    ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
}

// If a settings file exists but contains no valid settings - don't consider it
TEST(SettingsFile, InvalidSettingsFile) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    const char* explicit_layer_name = "VK_LAYER_TestLayer";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));
    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("foobarbaz")));
    auto check_integrity = [&env, explicit_layer_name, implicit_layer_name]() {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name));
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.create_info.add_layer(explicit_layer_name);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name));
    };

    {
        std::fstream fuzzer_output_json_file{FUZZER_OUTPUT_JSON_FILE, std::ios_base::in};
        ASSERT_TRUE(fuzzer_output_json_file.is_open());
        std::stringstream fuzzer_output_json;
        fuzzer_output_json << fuzzer_output_json_file.rdbuf();
        env.write_settings_file(fuzzer_output_json.str(), true);

        check_integrity();
    }

    // No actual settings
    {
        JsonWriter writer{};
        writer.StartObject();
        writer.AddKeyedString("file_format_version", "0.0.0");
        writer.EndObject();
        env.write_settings_file(writer.output, true);

        check_integrity();
    }

    {
        JsonWriter writer{};
        writer.StartObject();
        writer.AddKeyedString("file_format_version", "0.0.0");
        writer.StartKeyedArray("settings_array");
        writer.EndArray();
        writer.StartKeyedObject("settings");
        writer.EndObject();
        writer.EndObject();
        env.write_settings_file(writer.output, true);

        check_integrity();
    }

    {
        JsonWriter writer{};
        writer.StartObject();
        for (uint32_t i = 0; i < 3; i++) {
            writer.StartKeyedArray("settings_array");
            writer.EndArray();
            writer.StartKeyedObject("boogabooga");
            writer.EndObject();
            writer.StartKeyedObject("settings");
            writer.EndObject();
        }
        writer.EndObject();
        env.write_settings_file(writer.output, true);

        check_integrity();
    }
}

// Unknown layers are put in the correct location
TEST(SettingsFile, UnknownLayersInRightPlace) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    const char* explicit_layer_name1 = "VK_LAYER_TestLayer1";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));
    const char* implicit_layer_name1 = "VK_LAYER_ImplicitTestLayer1";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name1)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("foobarbaz")));
    const char* explicit_layer_name2 = "VK_LAYER_TestLayer2";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));
    const char* implicit_layer_name2 = "VK_LAYER_ImplicitTestLayer2";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name2)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("foobarbaz")));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name2)
                                         .set_path(env.get_shimmed_layer_manifest_path(2))
                                         .set_control("on"))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(implicit_layer_name2)
                                         .set_path(env.get_shimmed_layer_manifest_path(3))
                                         .set_control("on")
                                         .set_treat_as_implicit_manifest(true))));

    auto layer_props = env.GetLayerProperties(4);
    ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name2));
    ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name1));
    ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1));
    ASSERT_TRUE(string_eq(layer_props.at(3).layerName, implicit_layer_name2));
    InstWrapper inst{env.vulkan_functions};
    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
    inst.create_info.add_layer(explicit_layer_name1);
    inst.CheckCreate();
    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
    auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4);
    ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name2));
    ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name1));
    ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1));
    ASSERT_TRUE(string_eq(layer_props.at(3).layerName, implicit_layer_name2));
}

// Settings file allows loading multiple layers with the same name - as long as the path is different
TEST(SettingsFile, MultipleLayersWithSameName) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* explicit_layer_name = "VK_LAYER_TestLayer";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(explicit_layer_name)
                                                             .set_description("0000")
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(explicit_layer_name)
                                                             .set_description("1111")
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name)
                                         .set_path(env.get_shimmed_layer_manifest_path(0))
                                         .set_control("on"))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name)
                                         .set_path(env.get_shimmed_layer_manifest_path(1))
                                         .set_control("on"))));
    auto layer_props = env.GetLayerProperties(2);
    ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name));
    ASSERT_TRUE(string_eq(layer_props.at(0).description, "0000"));
    ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name));
    ASSERT_TRUE(string_eq(layer_props.at(1).description, "1111"));
    InstWrapper inst{env.vulkan_functions};
    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
    inst.CheckCreate();
    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
    auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
    ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name));
    ASSERT_TRUE(string_eq(layers.at(0).description, "0000"));
    ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name));
    ASSERT_TRUE(string_eq(layers.at(1).description, "1111"));
}

// Settings file shouldn't be able to cause the same layer from the same path twice
TEST(SettingsFile, MultipleLayersWithSamePath) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* explicit_layer_name = "VK_LAYER_TestLayer";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name)
                                         .set_path(env.get_shimmed_layer_manifest_path(0))
                                         .set_control("on"))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name)
                                         .set_path(env.get_shimmed_layer_manifest_path(0))
                                         .set_control("on"))));

    auto layer_props = env.GetLayerProperties(1);
    ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name));

    InstWrapper inst{env.vulkan_functions};
    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
    inst.CheckCreate();
    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
    auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
    ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name));
}

// Settings contains a layer whose name doesn't match the one found in the layer manifest - make sure the layer from the settings
// file is removed
TEST(SettingsFile, MismatchedLayerNameAndManifestPath) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* manifest_explicit_layer_name = "VK_LAYER_MANIFEST_TestLayer";
    const char* settings_explicit_layer_name = "VK_LAYER_Settings_TestLayer";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(manifest_explicit_layer_name)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* implicit_layer_name = "VK_LAYER_Implicit_TestLayer";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("oof")));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
            LoaderSettingsLayerConfiguration{}
                .set_name(settings_explicit_layer_name)
                .set_path(env.get_shimmed_layer_manifest_path(0))
                .set_control("on"))));

    ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));

    InstWrapper inst{env.vulkan_functions};
    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
    inst.CheckCreate();
    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
    ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
}

// Settings file should take precedence over the meta layer, if present
TEST(SettingsFile, ImplicitLayerWithEnableEnvironment) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* explicit_layer_1 = "VK_LAYER_Regular_TestLayer";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* implicit_layer_1 = "VK_LAYER_RegularImplicit_TestLayer";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_1)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("AndISaidHey")
                                                             .set_enable_environment("WhatsGoingOn")));

    const char* explicit_layer_2 = "VK_LAYER_Regular_TestLayer1";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* implicit_layer_2 = "VK_LAYER_RegularImplicit_TestLayer2";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_2)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("HeyHeyHeyyaya")
                                                             .set_enable_environment("HeyHeyHeyhey")));

    const char* explicit_layer_3 = "VK_LAYER_Regular_TestLayer3";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_3).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_1)
                                         .set_path(env.get_shimmed_layer_manifest_path(0))
                                         .set_control("auto")
                                         .set_treat_as_implicit_manifest(false))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(implicit_layer_1)
                                         .set_path(env.get_shimmed_layer_manifest_path(1))
                                         .set_control("auto")
                                         .set_treat_as_implicit_manifest(true))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_2)
                                         .set_path(env.get_shimmed_layer_manifest_path(2))
                                         .set_control("auto")
                                         .set_treat_as_implicit_manifest(false))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(implicit_layer_2)
                                         .set_path(env.get_shimmed_layer_manifest_path(3))
                                         .set_control("auto")
                                         .set_treat_as_implicit_manifest(true))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_3)
                                         .set_path(env.get_shimmed_layer_manifest_path(4))
                                         .set_control("on")
                                         .set_treat_as_implicit_manifest(false))));
    {
        auto layer_props = env.GetLayerProperties(5);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_1));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_2));
        ASSERT_TRUE(string_eq(layer_props.at(3).layerName, implicit_layer_2));
        ASSERT_TRUE(string_eq(layer_props.at(4).layerName, explicit_layer_3));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_3));
    }
    {
        EnvVarWrapper enable_meta_layer{"WhatsGoingOn", "1"};
        auto layer_props = env.GetLayerProperties(5);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_1));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_2));
        ASSERT_TRUE(string_eq(layer_props.at(3).layerName, implicit_layer_2));
        ASSERT_TRUE(string_eq(layer_props.at(4).layerName, explicit_layer_3));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_3));
    }
    {
        EnvVarWrapper enable_meta_layer{"HeyHeyHeyhey", "1"};
        auto layer_props = env.GetLayerProperties(5);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_1));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_2));
        ASSERT_TRUE(string_eq(layer_props.at(3).layerName, implicit_layer_2));
        ASSERT_TRUE(string_eq(layer_props.at(4).layerName, explicit_layer_3));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_2));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_3));
    }
}

// Settings file should take precedence over the meta layer, if present
TEST(SettingsFile, MetaLayerAlsoActivates) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* settings_explicit_layer_name = "VK_LAYER_Regular_TestLayer";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(settings_explicit_layer_name)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* settings_implicit_layer_name = "VK_LAYER_RegularImplicit_TestLayer";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(settings_implicit_layer_name)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("AndISaidHey")
                                                             .set_enable_environment("WhatsGoingOn")));

    const char* component_explicit_layer_name1 = "VK_LAYER_Component_TestLayer1";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(component_explicit_layer_name1)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* component_explicit_layer_name2 = "VK_LAYER_Component_TestLayer2";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(component_explicit_layer_name2)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* meta_layer_name1 = "VK_LAYER_meta_layer1";
    env.add_implicit_layer(
        {}, ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer(ManifestLayer::LayerDescription{}
                                                                             .set_name(meta_layer_name1)
                                                                             .add_component_layer(component_explicit_layer_name2)
                                                                             .add_component_layer(component_explicit_layer_name1)
                                                                             .set_disable_environment("NotGonnaWork")));

    const char* meta_layer_name2 = "VK_LAYER_meta_layer2";
    env.add_implicit_layer(
        {}, ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer(ManifestLayer::LayerDescription{}
                                                                             .set_name(meta_layer_name2)
                                                                             .add_component_layer(component_explicit_layer_name1)
                                                                             .set_disable_environment("ILikeTrains")
                                                                             .set_enable_environment("BakedBeans")));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(settings_explicit_layer_name)
                                         .set_path(env.get_shimmed_layer_manifest_path(0))
                                         .set_control("on")
                                         .set_treat_as_implicit_manifest(false))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(settings_implicit_layer_name)
                                         .set_path(env.get_shimmed_layer_manifest_path(1))
                                         .set_control("auto")
                                         .set_treat_as_implicit_manifest(true))));
    {
        EnvVarWrapper enable_meta_layer{"WhatsGoingOn", "1"};
        auto layer_props = env.GetLayerProperties(6);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, settings_explicit_layer_name));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, meta_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, meta_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(3).layerName, component_explicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(4).layerName, component_explicit_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(5).layerName, settings_implicit_layer_name));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 5);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, settings_explicit_layer_name));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, component_explicit_layer_name2));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, component_explicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(3).layerName, meta_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(4).layerName, settings_implicit_layer_name));
    }
    {
        EnvVarWrapper enable_meta_layer{"BakedBeans", "1"};
        auto layer_props = env.GetLayerProperties(6);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, settings_explicit_layer_name));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, meta_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, meta_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(3).layerName, component_explicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(4).layerName, component_explicit_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(5).layerName, settings_implicit_layer_name));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 5);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, settings_explicit_layer_name));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, component_explicit_layer_name2));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, component_explicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(3).layerName, meta_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(4).layerName, meta_layer_name2));
    }
}

TEST(SettingsFile, DuplicateMetaLayers) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* explicit_layer_name = "VK_LAYER_Regular_TestLayer";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));
    // Add an implicit layer and a meta layer that share a name
    const char* meta_layer_name = "VK_LAYER_meta_layer";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(meta_layer_name)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("NotGonnaWork")));
    env.add_implicit_layer(
        {}, ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer(ManifestLayer::LayerDescription{}
                                                                             .set_name(meta_layer_name)
                                                                             .add_component_layer(explicit_layer_name)
                                                                             .set_disable_environment("NotGonnaWork")));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name)
                                         .set_path(env.get_shimmed_layer_manifest_path(0))
                                         .set_control("auto")
                                         .set_treat_as_implicit_manifest(false))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"))
            .add_layer_configuration(
                LoaderSettingsLayerConfiguration{}
                    .set_name(meta_layer_name)
                    .set_path(env.get_folder(ManifestLocation::implicit_layer).location() / "meta_test_layer.json")
                    .set_control("auto")
                    .set_treat_as_implicit_manifest(true))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(meta_layer_name)
                                         .set_path(env.get_shimmed_layer_manifest_path(1))
                                         .set_control("auto")
                                         .set_treat_as_implicit_manifest(true))

            ));
    InstWrapper inst{env.vulkan_functions};
    inst.CheckCreate();
}

// Layers are correctly ordered by settings file.
TEST(SettingsFile, LayerOrdering) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* explicit_layer_name2 = "VK_LAYER_Regular_TestLayer2";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name1)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("Domierigato")));

    const char* implicit_layer_name2 = "VK_LAYER_Implicit_TestLayer2";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name2)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("Mistehrobato")));

    env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all"));

    std::vector<LoaderSettingsLayerConfiguration> layer_configs{4};
    layer_configs.at(0)
        .set_name(explicit_layer_name1)
        .set_path(env.get_shimmed_layer_manifest_path(0))
        .set_control("on")
        .set_treat_as_implicit_manifest(false);
    layer_configs.at(1)
        .set_name(explicit_layer_name2)
        .set_path(env.get_shimmed_layer_manifest_path(1))
        .set_control("on")
        .set_treat_as_implicit_manifest(false);
    layer_configs.at(2)
        .set_name(implicit_layer_name1)
        .set_path(env.get_shimmed_layer_manifest_path(2))
        .set_control("on")
        .set_treat_as_implicit_manifest(true);
    layer_configs.at(3)
        .set_name(implicit_layer_name2)
        .set_path(env.get_shimmed_layer_manifest_path(3))
        .set_control("on")
        .set_treat_as_implicit_manifest(true);

    std::sort(layer_configs.begin(), layer_configs.end());
    uint32_t permutation_count = 0;
    do {
        env.loader_settings.app_specific_settings.at(0).layer_configurations.clear();
        env.loader_settings.app_specific_settings.at(0).add_layer_configurations(layer_configs);
        env.update_loader_settings(env.loader_settings);

        auto layer_props = env.GetLayerProperties(4);
        for (uint32_t i = 0; i < 4; i++) {
            ASSERT_TRUE(layer_configs.at(i).name == layer_props.at(i).layerName);
        }

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto active_layers = inst.GetActiveLayers(inst.GetPhysDev(), 4);
        for (uint32_t i = 0; i < 4; i++) {
            ASSERT_TRUE(layer_configs.at(i).name == active_layers.at(i).layerName);
        }
        env.debug_log.clear();
        permutation_count++;
    } while (std::next_permutation(layer_configs.begin(), layer_configs.end()));
    ASSERT_EQ(permutation_count, 24U);  // should be this many orderings
}

TEST(SettingsFile, EnvVarsWork_VK_LAYER_PATH) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::env_var),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name1)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("Domierigato")));
    const char* non_env_var_layer_name2 = "VK_LAYER_Regular_TestLayer2";
    env.add_explicit_layer(
        {},
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(non_env_var_layer_name2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name1));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
    }
    {
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.create_info.add_layer(explicit_layer_name1);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name1));
    }
    env.update_loader_settings(env.loader_settings.add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(non_env_var_layer_name2)
                                         .set_control("on")
                                         .set_path(env.get_shimmed_layer_manifest_path(2)))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"))));
    {
        auto layer_props = env.GetLayerProperties(3);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, non_env_var_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, non_env_var_layer_name2));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name1));
    }
    {
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.create_info.add_layer(explicit_layer_name1);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, non_env_var_layer_name2));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, explicit_layer_name1));
    }
}

TEST(SettingsFile, EnvVarsWork_VK_ADD_LAYER_PATH) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name1)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("Domierigato")));
    const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::add_env_var),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* non_env_var_layer_name2 = "VK_LAYER_Regular_TestLayer2";
    env.add_explicit_layer(
        {},
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(non_env_var_layer_name2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    {
        auto layer_props = env.GetLayerProperties(3);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, non_env_var_layer_name2));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
    }
    {
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.create_info.add_layer(explicit_layer_name1);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name1));
    }

    env.update_loader_settings(env.loader_settings.add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name1)
                                         .set_control("on")
                                         .set_path(env.get_shimmed_layer_manifest_path(1)))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(non_env_var_layer_name2)
                                         .set_control("on")
                                         .set_path(env.get_shimmed_layer_manifest_path(2)))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(implicit_layer_name1)
                                         .set_control("on")
                                         .set_path(env.get_shimmed_layer_manifest_path(0))
                                         .set_treat_as_implicit_manifest(true))));
    {
        auto layer_props = env.GetLayerProperties(3);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, non_env_var_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, implicit_layer_name1));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, non_env_var_layer_name2));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, implicit_layer_name1));
    }
    {
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.create_info.add_layer(explicit_layer_name1);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, non_env_var_layer_name2));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, implicit_layer_name1));
    }
}

TEST(SettingsFile, EnvVarsWork_VK_IMPLICIT_LAYER_PATH) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::env_var),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name1)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("Domierigato")));
    const char* settings_layer_path = "VK_LAYER_Regular_TestLayer2";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(settings_layer_path)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("Domierigato")));

    {
        auto layer_props = env.GetLayerProperties(3);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, settings_layer_path));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_path));
    }
    {
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.create_info.add_layer(explicit_layer_name1);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_path));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, explicit_layer_name1));
    }
    env.update_loader_settings(env.loader_settings.add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(settings_layer_path)
                                         .set_control("on")
                                         .set_path(env.get_shimmed_layer_manifest_path(2))
                                         .set_treat_as_implicit_manifest(true))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"))));
    {
        auto layer_props = env.GetLayerProperties(3);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, settings_layer_path));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, settings_layer_path));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name1));
    }
    {
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.create_info.add_layer(explicit_layer_name1);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, settings_layer_path));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, explicit_layer_name1));
    }
}

TEST(SettingsFile, EnvVarsWork_VK_ADD_IMPLICIT_LAYER_PATH) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name1)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("Domierigato")));
    const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::add_env_var),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* settings_layer_name = "VK_LAYER_Regular_TestLayer2";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(settings_layer_name)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("gozaimasu")));

    {
        auto layer_props = env.GetLayerProperties(3);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, settings_layer_name));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_name));
    }
    {
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.create_info.add_layer(explicit_layer_name1);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_name));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, explicit_layer_name1));
    }

    env.update_loader_settings(env.loader_settings.add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name1)
                                         .set_control("on")
                                         .set_path(env.get_shimmed_layer_manifest_path(1)))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(settings_layer_name)
                                         .set_control("on")
                                         .set_path(env.get_shimmed_layer_manifest_path(2))
                                         .set_treat_as_implicit_manifest(true))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(implicit_layer_name1)
                                         .set_control("on")
                                         .set_path(env.get_shimmed_layer_manifest_path(0))
                                         .set_treat_as_implicit_manifest(true))));
    {
        auto layer_props = env.GetLayerProperties(3);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, settings_layer_name));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, implicit_layer_name1));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_name));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, implicit_layer_name1));
    }
    {
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.create_info.add_layer(explicit_layer_name1);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_name));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, implicit_layer_name1));
    }
}

TEST(SettingsFile, EnvVarsWork_VK_INSTANCE_LAYERS) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* filler_layer_name = "VK_LAYER_filler";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(filler_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* explicit_layer_name = "VK_LAYER_Regular_TestLayer1";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    {
        EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name};
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, filler_layer_name));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layer = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layer.at(0).layerName, explicit_layer_name));
    }
    env.update_loader_settings(env.loader_settings.add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(filler_layer_name)
                                         .set_control("auto")
                                         .set_path(env.get_shimmed_layer_manifest_path(0)))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name)
                                         .set_control("off")
                                         .set_path(env.get_shimmed_layer_manifest_path(1)))));
    {
        auto layer_props = env.GetLayerProperties(1);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, filler_layer_name));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
    }
    EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name};
    {
        auto layer_props = env.GetLayerProperties(1);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, filler_layer_name));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
    }
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).control = "auto";
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, filler_layer_name));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name));
    }
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).control = "on";
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, filler_layer_name));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name));

        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name));
    }
}

TEST(SettingsFile, EnvVarsWork_VK_INSTANCE_LAYERS_multiple_layers) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* explicit_layer_name2 = "VK_LAYER_Regular_TestLayer2";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* explicit_layer_name3 = "VK_LAYER_Regular_TestLayer3";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name3).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS"};
    vk_instance_layers.add_to_list(explicit_layer_name2);
    vk_instance_layers.add_to_list(explicit_layer_name1);
    {
        auto layer_props = env.GetLayerProperties(3);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name3));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layer = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layer.at(0).layerName, explicit_layer_name2));
        ASSERT_TRUE(string_eq(layer.at(1).layerName, explicit_layer_name1));
    }
    env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configurations(
        {LoaderSettingsLayerConfiguration{}
             .set_name(explicit_layer_name1)
             .set_control("off")
             .set_path(env.get_shimmed_layer_manifest_path(0)),
         LoaderSettingsLayerConfiguration{}
             .set_name(explicit_layer_name2)
             .set_control("off")
             .set_path(env.get_shimmed_layer_manifest_path(1)),
         LoaderSettingsLayerConfiguration{}
             .set_name(explicit_layer_name3)
             .set_control("auto")
             .set_path(env.get_shimmed_layer_manifest_path(2))}));
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(1);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name3));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
    }
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).control = "auto";
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1));
    }
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).control = "on";
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3));

        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1));
    }
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).control = "off";
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).control = "auto";
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();
        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name2));
    }
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).control = "on";
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3));

        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name2));
    }
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).control = "auto";
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3));

        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name2));
    }
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).control = "on";
    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(3);
        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name2));
        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name3));

        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name2));
    }
}

// Make sure that layers disabled by settings file aren't enabled by VK_LOADER_LAYERS_ENABLE
TEST(SettingsFile, EnvVarsWork_VK_LOADER_LAYERS_ENABLE) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all"));
    const char* explicit_layer_name = add_layer_and_settings(env, "VK_LAYER_Regular_TestLayer1", LayerType::exp, "off");
    env.update_loader_settings(env.loader_settings);

    EnvVarWrapper vk_instance_layers{"VK_LOADER_LAYERS_ENABLE", explicit_layer_name};
    ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));

    InstWrapper inst{env.vulkan_functions};
    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
    inst.CheckCreate();
    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
    ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
}

TEST(SettingsFile, settings_disable_layer_enabled_by_env_vars) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all"));
    std::vector<const char*> layer_names;
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_alpha", LayerType::imp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_bravo", LayerType::imp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_charlie", LayerType::imp, "auto"));
    env.loader_settings.app_specific_settings.back().add_layer_configuration(
        LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_delta", LayerType::exp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_echo", LayerType::exp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_foxtrot", LayerType::exp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_gamma", LayerType::exp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_indigo", LayerType::exp, "auto"));
    auto disable_layer_name = add_layer_and_settings(env, "VK_LAYER_juniper", LayerType::exp, "off");
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_kangaroo", LayerType::exp, "on"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_lima", LayerType::exp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_mango", LayerType::exp, "auto"));

    env.update_loader_settings(env.loader_settings);
    {
        EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", disable_layer_name};
        auto layer_props = env.GetLayerProperties(11);
        for (size_t i = 0; i < layer_props.size(); i++) {
            ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName));
        }
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4);
        ASSERT_TRUE(string_eq(layer_names.at(0), layers.at(0).layerName));
        ASSERT_TRUE(string_eq(layer_names.at(1), layers.at(1).layerName));
        ASSERT_TRUE(string_eq(layer_names.at(2), layers.at(2).layerName));
        ASSERT_TRUE(string_eq(layer_names.at(8), layers.at(3).layerName));
    }
    {
        EnvVarWrapper vk_instance_layers{"VK_LOADER_LAYERS_ENABLE", disable_layer_name};
        auto layer_props = env.GetLayerProperties(11);
        for (size_t i = 0; i < layer_props.size(); i++) {
            ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName));
        }
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4);
        ASSERT_TRUE(string_eq(layer_names.at(0), layers.at(0).layerName));
        ASSERT_TRUE(string_eq(layer_names.at(1), layers.at(1).layerName));
        ASSERT_TRUE(string_eq(layer_names.at(2), layers.at(2).layerName));
        ASSERT_TRUE(string_eq(layer_names.at(8), layers.at(3).layerName));
    }
    {
        auto layer_props = env.GetLayerProperties(11);
        for (size_t i = 0; i < layer_props.size(); i++) {
            ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName));
        }
        InstWrapper inst{env.vulkan_functions};
        inst.create_info.add_layer(disable_layer_name);
        inst.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT);
    }
}

// Make sure that layers enabled by settings file aren't disabled by VK_LOADER_LAYERS_ENABLE
TEST(SettingsFile, EnvVarsWork_VK_LOADER_LAYERS_DISABLE) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* explicit_layer_name = "VK_LAYER_Regular_TestLayer1";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.update_loader_settings(
        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
            LoaderSettingsLayerConfiguration{}
                .set_name(explicit_layer_name)
                .set_control("on")
                .set_path(env.get_shimmed_layer_manifest_path(0)))));

    EnvVarWrapper vk_instance_layers{"VK_LOADER_LAYERS_DISABLE", explicit_layer_name};
    auto layer_props = env.GetLayerProperties(1);
    ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name));

    InstWrapper inst{env.vulkan_functions};
    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
    inst.CheckCreate();
    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
    auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
    ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name));
}

#if defined(WIN32)
TEST(SettingsFile, MultipleKeysInRegistryInUnsecureLocation) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    env.platform_shim->add_unsecured_manifest_to_registry(ManifestCategory::settings, "jank_path");
    env.platform_shim->add_unsecured_manifest_to_registry(ManifestCategory::settings, "jank_path2");

    const char* regular_layer_name = "VK_LAYER_TestLayer_0";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.update_loader_settings(
        env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
            AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                                                           .set_name(regular_layer_name)
                                                                                           .set_path(env.get_layer_manifest_path())
                                                                                           .set_control("on"))),
        false);
    env.add_icd(TEST_ICD_PATH_VERSION_2);

    auto layer_props = env.GetLayerProperties(1);
    EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name));

    InstWrapper inst{env.vulkan_functions};
    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
    inst.CheckCreate();

    ASSERT_TRUE(env.debug_log.find(get_unsecure_settings_location_log_message(env)));
    auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
    ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name));
}

TEST(SettingsFile, MultipleKeysInRegistryInSecureLocation) {
    FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    env.platform_shim->add_manifest_to_registry(ManifestCategory::settings, "jank_path");
    env.platform_shim->add_manifest_to_registry(ManifestCategory::settings, "jank_path2");

    const char* regular_layer_name = "VK_LAYER_TestLayer_0";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                                                       .set_name(regular_layer_name)
                                                                                       .set_path(env.get_layer_manifest_path())
                                                                                       .set_control("on"))));
    env.add_icd(TEST_ICD_PATH_VERSION_2);

    // Make sure it works if the settings file is in the HKEY_LOCAL_MACHINE
    env.add_icd(TEST_ICD_PATH_VERSION_2);
    {
        auto layer_props = env.GetLayerProperties(1);
        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name));

        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();

        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name));
    }
}
#endif

// Preinstance functions respect the settings file
TEST(SettingsFile, PreInstanceFunctions) {
    FrameworkEnvironment env;
    env.add_icd(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).add_physical_device({});

    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";

    env.add_implicit_layer(
        {}, ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer(
                ManifestLayer::LayerDescription{}
                    .set_name(implicit_layer_name)
                    .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                    .set_disable_environment("DISABLE_ME")
                    .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                   .set_vk_func("vkEnumerateInstanceLayerProperties")
                                                   .set_override_name("test_preinst_vkEnumerateInstanceLayerProperties"))
                    .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                   .set_vk_func("vkEnumerateInstanceExtensionProperties")
                                                   .set_override_name("test_preinst_vkEnumerateInstanceExtensionProperties"))
                    .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                   .set_vk_func("vkEnumerateInstanceVersion")
                                                   .set_override_name("test_preinst_vkEnumerateInstanceVersion"))));

    env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
        LoaderSettingsLayerConfiguration{}
            .set_name(implicit_layer_name)
            .set_control("on")
            .set_path(env.get_shimmed_layer_manifest_path(0))
            .set_treat_as_implicit_manifest(true)));
    env.update_loader_settings(env.loader_settings);
    {
        auto& layer = env.get_test_layer(0);
        // Check layer props
        uint32_t layer_props = 43;
        layer.set_reported_layer_props(layer_props);

        uint32_t count = 0;
        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
        ASSERT_EQ(count, layer_props);

        // check extension props
        uint32_t ext_props = 52;
        layer.set_reported_extension_props(ext_props);
        count = 0;
        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr));
        ASSERT_EQ(count, ext_props);

        // check version
        uint32_t layer_version = VK_MAKE_API_VERSION(1, 2, 3, 4);
        layer.set_reported_instance_version(layer_version);

        uint32_t version = 0;
        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceVersion(&version));
        ASSERT_EQ(version, layer_version);
    }
    // control is set to off
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("off");
    env.update_loader_settings(env.loader_settings);

    {
        auto& layer = env.get_test_layer(0);
        // Check layer props
        uint32_t layer_props = 43;
        layer.set_reported_layer_props(layer_props);

        uint32_t count = 0;
        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
        ASSERT_EQ(count, 0U);  // dont use the intercepted count

        // check extension props
        uint32_t ext_props = 52;
        layer.set_reported_extension_props(ext_props);
        count = 0;
        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr));
        ASSERT_EQ(count, 4U);  // dont use the intercepted count - use default count

        // check version
        uint32_t layer_version = VK_MAKE_API_VERSION(1, 2, 3, 4);
        layer.set_reported_instance_version(layer_version);

        uint32_t version = 0;
        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceVersion(&version));
        ASSERT_EQ(version, VK_HEADER_VERSION_COMPLETE);
    }

    // control is set to auto
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("auto");
    env.update_loader_settings(env.loader_settings);

    {
        auto& layer = env.get_test_layer(0);
        // Check layer props
        uint32_t layer_props = 43;
        layer.set_reported_layer_props(layer_props);

        uint32_t count = 0;
        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
        ASSERT_EQ(count, layer_props);

        // check extension props
        uint32_t ext_props = 52;
        layer.set_reported_extension_props(ext_props);
        count = 0;
        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr));
        ASSERT_EQ(count, ext_props);

        // check version
        uint32_t layer_version = VK_MAKE_API_VERSION(1, 2, 3, 4);
        layer.set_reported_instance_version(layer_version);

        uint32_t version = 0;
        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceVersion(&version));
        ASSERT_EQ(version, layer_version);
    }
}

// If an implicit layer's disable environment variable is set, but the settings file says to turn the layer on, the layer should be
// activated.
TEST(SettingsFile, ImplicitLayerDisableEnvironmentVariableOverriden) {
    FrameworkEnvironment env;
    env.add_icd(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).add_physical_device({});

    const char* filler_layer_name = "VK_LAYER_filler";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(filler_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(implicit_layer_name)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                             .set_disable_environment("DISABLE_ME")
                                                             .set_enable_environment("ENABLE_ME")));
    env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configurations(
        {LoaderSettingsLayerConfiguration{}
             .set_name(filler_layer_name)
             .set_path(env.get_shimmed_layer_manifest_path(0))
             .set_control("auto")
             .set_treat_as_implicit_manifest(false),
         LoaderSettingsLayerConfiguration{}
             .set_name(implicit_layer_name)
             .set_path(env.get_shimmed_layer_manifest_path(1))
             .set_control("auto")
             .set_treat_as_implicit_manifest(true)}));

    auto check_log_for_insert_instance_layer_string = [&env, implicit_layer_name](bool check_for_enable) {
        {
            InstWrapper inst{env.vulkan_functions};
            FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
            inst.CheckCreate();
            if (check_for_enable) {
                ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + implicit_layer_name));
                auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
                ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name));
            } else {
                ASSERT_FALSE(env.debug_log.find(std::string("Insert instance layer \"") + implicit_layer_name));
                ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
            }
        }
        env.debug_log.clear();
    };

    // control is set to on
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).set_control("on");
    env.update_loader_settings(env.loader_settings);
    {
        EnvVarWrapper enable_env_var{"ENABLE_ME"};
        EnvVarWrapper disable_env_var{"DISABLE_ME"};

        auto layers = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layers[0].layerName, filler_layer_name));
        ASSERT_TRUE(string_eq(layers[1].layerName, implicit_layer_name));

        check_log_for_insert_instance_layer_string(true);

        enable_env_var.set_new_value("0");
        check_log_for_insert_instance_layer_string(true);

        enable_env_var.set_new_value("1");
        check_log_for_insert_instance_layer_string(true);

        enable_env_var.remove_value();

        disable_env_var.set_new_value("0");
        check_log_for_insert_instance_layer_string(true);

        disable_env_var.set_new_value("1");
        check_log_for_insert_instance_layer_string(true);

        enable_env_var.set_new_value("1");
        disable_env_var.set_new_value("1");
        check_log_for_insert_instance_layer_string(true);
    }

    // control is set to off
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).set_control("off");
    env.update_loader_settings(env.loader_settings);
    {
        EnvVarWrapper enable_env_var{"ENABLE_ME"};
        EnvVarWrapper disable_env_var{"DISABLE_ME"};

        auto layers = env.GetLayerProperties(1);
        ASSERT_TRUE(string_eq(layers[0].layerName, filler_layer_name));

        check_log_for_insert_instance_layer_string(false);

        enable_env_var.set_new_value("0");
        check_log_for_insert_instance_layer_string(false);

        enable_env_var.set_new_value("1");
        check_log_for_insert_instance_layer_string(false);

        enable_env_var.remove_value();

        disable_env_var.set_new_value("0");
        check_log_for_insert_instance_layer_string(false);

        disable_env_var.set_new_value("1");
        check_log_for_insert_instance_layer_string(false);

        enable_env_var.set_new_value("1");
        disable_env_var.set_new_value("1");
        check_log_for_insert_instance_layer_string(false);
    }

    // control is set to auto
    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).set_control("auto");
    env.update_loader_settings(env.loader_settings);
    {
        EnvVarWrapper enable_env_var{"ENABLE_ME"};
        EnvVarWrapper disable_env_var{"DISABLE_ME"};

        auto layers = env.GetLayerProperties(2);
        ASSERT_TRUE(string_eq(layers[0].layerName, filler_layer_name));
        ASSERT_TRUE(string_eq(layers[1].layerName, implicit_layer_name));

        check_log_for_insert_instance_layer_string(false);

        enable_env_var.set_new_value("0");
        check_log_for_insert_instance_layer_string(false);

        enable_env_var.set_new_value("1");
        check_log_for_insert_instance_layer_string(true);

        enable_env_var.remove_value();

        disable_env_var.set_new_value("0");
        check_log_for_insert_instance_layer_string(false);

        disable_env_var.set_new_value("1");
        check_log_for_insert_instance_layer_string(false);

        enable_env_var.set_new_value("1");
        disable_env_var.set_new_value("1");
        check_log_for_insert_instance_layer_string(false);
    }
}

TEST(SettingsFile, ImplicitLayersNotAccidentallyEnabled) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all"));
    std::vector<const char*> layer_names;

    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_alpha", LayerType::imp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_bravo", LayerType::imp_with_enable_env, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_charlie", LayerType::imp_with_enable_env, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_delta", LayerType::imp_with_enable_env, "auto"));

    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_echo", LayerType::exp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_foxtrot", LayerType::exp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_gamma", LayerType::exp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_indigo", LayerType::exp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_juniper", LayerType::exp, "auto"));

    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_kangaroo", LayerType::exp, "on"));

    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_lima", LayerType::exp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_mango", LayerType::exp, "auto"));
    layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_niagara", LayerType::exp, "auto"));

    env.update_loader_settings(env.loader_settings);
    {
        auto layer_props = env.GetLayerProperties(13);
        for (size_t i = 0; i < layer_props.size(); i++) {
            ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName));
        }
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_names.at(0)));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, layer_names.at(9)));
    }

    {
        EnvVarWrapper env_var{"MUSHROOM1", "1"};
        auto layer_props = env.GetLayerProperties(13);
        for (size_t i = 0; i < layer_props.size(); i++) {
            ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName));
        }
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_names.at(0)));
        ASSERT_TRUE(string_eq(layers.at(1).layerName, layer_names.at(1)));
        ASSERT_TRUE(string_eq(layers.at(2).layerName, layer_names.at(9)));
    }
    {
        EnvVarWrapper env_var{"BADGER0", "1"};
        auto layer_props = env.GetLayerProperties(13);
        for (size_t i = 0; i < layer_props.size(); i++) {
            ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName));
        }
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_names.at(9)));
    }
}

TEST(SettingsFile, ImplicitLayersPreInstanceEnumInstLayerProps) {
    FrameworkEnvironment env;
    env.add_icd(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA);
    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";

    env.add_implicit_layer(
        {}, ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer(
                ManifestLayer::LayerDescription{}
                    .set_name(implicit_layer_name)
                    .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                    .set_disable_environment("DISABLE_ME")
                    .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                   .set_vk_func("vkEnumerateInstanceLayerProperties")
                                                   .set_override_name("test_preinst_vkEnumerateInstanceLayerProperties"))));

    env.update_loader_settings(
        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
            LoaderSettingsLayerConfiguration{}
                .set_name(implicit_layer_name)
                .set_path(env.get_shimmed_layer_manifest_path(0))
                .set_control("auto")
                .set_treat_as_implicit_manifest(true))));

    uint32_t layer_props = 43;
    auto& layer = env.get_test_layer(0);
    layer.set_reported_layer_props(layer_props);

    uint32_t count = 0;
    ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
    ASSERT_EQ(count, layer_props);
}

TEST(SettingsFile, EnableEnvironmentImplicitLayersPreInstanceEnumInstLayerProps) {
    FrameworkEnvironment env;
    env.add_icd(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA);
    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";

    env.add_implicit_layer(
        {}, ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer(
                ManifestLayer::LayerDescription{}
                    .set_name(implicit_layer_name)
                    .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                    .set_disable_environment("DISABLE_ME")
                    .set_enable_environment("ENABLE_ME")
                    .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                   .set_vk_func("vkEnumerateInstanceLayerProperties")
                                                   .set_override_name("test_preinst_vkEnumerateInstanceLayerProperties"))));

    env.update_loader_settings(
        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
            LoaderSettingsLayerConfiguration{}
                .set_name(implicit_layer_name)
                .set_path(env.get_shimmed_layer_manifest_path(0))
                .set_control("auto")
                .set_treat_as_implicit_manifest(true))));

    uint32_t layer_props = 43;
    auto& layer = env.get_test_layer(0);
    layer.set_reported_layer_props(layer_props);

    uint32_t count = 0;
    ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
    ASSERT_EQ(count, 1U);
    std::array<VkLayerProperties, 1> layers{};
    ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, layers.data()));
    ASSERT_EQ(count, 1U);
    ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name));
}

TEST(SettingsFile, ImplicitLayersPreInstanceEnumInstExtProps) {
    FrameworkEnvironment env;
    env.add_icd(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA);
    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";

    env.add_implicit_layer(
        {}, ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer(
                ManifestLayer::LayerDescription{}
                    .set_name(implicit_layer_name)
                    .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                    .set_disable_environment("DISABLE_ME")
                    .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                   .set_vk_func("vkEnumerateInstanceExtensionProperties")
                                                   .set_override_name("test_preinst_vkEnumerateInstanceExtensionProperties"))));

    env.update_loader_settings(
        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
            LoaderSettingsLayerConfiguration{}
                .set_name(implicit_layer_name)
                .set_path(env.get_shimmed_layer_manifest_path(0))
                .set_control("auto")
                .set_treat_as_implicit_manifest(true))));

    uint32_t ext_props = 52;
    auto& layer = env.get_test_layer(0);
    layer.set_reported_extension_props(ext_props);

    uint32_t count = 0;
    ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr));
    ASSERT_EQ(count, ext_props);
}

TEST(SettingsFile, EnableEnvironmentImplicitLayersPreInstanceEnumInstExtProps) {
    FrameworkEnvironment env;
    env.add_icd(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA);
    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";

    env.add_implicit_layer(
        {}, ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer(
                ManifestLayer::LayerDescription{}
                    .set_name(implicit_layer_name)
                    .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                    .set_disable_environment("DISABLE_ME")
                    .set_enable_environment("ENABLE_ME")
                    .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                   .set_vk_func("vkEnumerateInstanceExtensionProperties")
                                                   .set_override_name("test_preinst_vkEnumerateInstanceExtensionProperties"))));

    env.update_loader_settings(
        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
            LoaderSettingsLayerConfiguration{}
                .set_name(implicit_layer_name)
                .set_path(env.get_shimmed_layer_manifest_path(0))
                .set_control("auto")
                .set_treat_as_implicit_manifest(true))));

    uint32_t ext_props = 52;
    auto& layer = env.get_test_layer(0);
    layer.set_reported_extension_props(ext_props);

    auto extensions = env.GetInstanceExtensions(4);
    EXPECT_TRUE(string_eq(extensions.at(0).extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME));
    EXPECT_TRUE(string_eq(extensions.at(1).extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME));
    EXPECT_TRUE(string_eq(extensions.at(2).extensionName, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME));
    EXPECT_TRUE(string_eq(extensions.at(3).extensionName, VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME));
}

TEST(SettingsFile, ImplicitLayersPreInstanceVersion) {
    FrameworkEnvironment env;
    env.add_icd(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)
        .add_physical_device({})
        .set_icd_api_version(VK_MAKE_API_VERSION(0, 1, 2, 3));

    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";

    env.add_implicit_layer({},
                           ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer(
                               ManifestLayer::LayerDescription{}
                                   .set_name(implicit_layer_name)
                                   .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                   .set_api_version(VK_MAKE_API_VERSION(0, 1, 2, 3))
                                   .set_disable_environment("DISABLE_ME")
                                   .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                                  .set_vk_func("vkEnumerateInstanceVersion")
                                                                  .set_override_name("test_preinst_vkEnumerateInstanceVersion"))));

    env.update_loader_settings(
        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
            LoaderSettingsLayerConfiguration{}
                .set_name(implicit_layer_name)
                .set_path(env.get_shimmed_layer_manifest_path(0))
                .set_control("auto")
                .set_treat_as_implicit_manifest(true))));

    uint32_t layer_version = VK_MAKE_API_VERSION(1, 2, 3, 4);
    auto& layer = env.get_test_layer(0);
    layer.set_reported_instance_version(layer_version);

    uint32_t version = 0;
    ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceVersion(&version));
    ASSERT_EQ(version, layer_version);
}

TEST(SettingsFile, EnableEnvironmentImplicitLayersPreInstanceVersion) {
    FrameworkEnvironment env;
    env.add_icd(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)
        .add_physical_device({})
        .set_icd_api_version(VK_MAKE_API_VERSION(0, 1, 2, 3));

    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";

    env.add_implicit_layer({},
                           ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer(
                               ManifestLayer::LayerDescription{}
                                   .set_name(implicit_layer_name)
                                   .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                   .set_api_version(VK_MAKE_API_VERSION(0, 1, 2, 3))
                                   .set_disable_environment("DISABLE_ME")
                                   .set_enable_environment("ENABLE_ME")
                                   .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                                  .set_vk_func("vkEnumerateInstanceVersion")
                                                                  .set_override_name("test_preinst_vkEnumerateInstanceVersion"))));

    env.update_loader_settings(
        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
            LoaderSettingsLayerConfiguration{}
                .set_name(implicit_layer_name)
                .set_path(env.get_shimmed_layer_manifest_path(0))
                .set_control("auto")
                .set_treat_as_implicit_manifest(true))));

    uint32_t layer_version = VK_MAKE_API_VERSION(1, 2, 3, 4);
    auto& layer = env.get_test_layer(0);
    layer.set_reported_instance_version(layer_version);

    uint32_t version = 0;
    ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceVersion(&version));
    ASSERT_EQ(version, VK_HEADER_VERSION_COMPLETE);
}

// Settings can say which filters to use - make sure those are propagated & treated correctly
TEST(SettingsFile, StderrLogFilters) {
    FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("")};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    const char* explicit_layer_name = "Regular_TestLayer1";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name)
                                         .set_path(env.get_shimmed_layer_manifest_path())
                                         .set_control("on"))
            .add_layer_configuration(
                LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("on"))));

    std::string expected_output_verbose;
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Layer Configurations count = 2\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          ---- Layer Configuration [0] ----\n";
    expected_output_verbose += std::string("[Vulkan Loader] DEBUG:          Name: ") + explicit_layer_name + "\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Path: " + env.get_shimmed_layer_manifest_path().string() + "\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Layer Type: Explicit\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Control: on\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          ---- Layer Configuration [1] ----\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Name: VK_LAYER_missing\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Path: /road/to/nowhere\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Layer Type: Explicit\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Control: on\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          ---------------------------------\n";

    std::string expected_output_info =
        std::string("[Vulkan Loader] INFO:           ") + get_settings_location_log_message(env) + "\n";

    std::string expected_output_warning = "[Vulkan Loader] WARNING:        Layer name " + std::string(explicit_layer_name) +
                                          " does not conform to naming standard (Policy #LLP_LAYER_3)\n";

    std::string expected_output_error =
        "[Vulkan Loader] ERROR:          loader_get_json: Failed to open JSON file /road/to/nowhere\n";

    env.loader_settings.app_specific_settings.at(0).stderr_log = {"all"};
    env.update_loader_settings(env.loader_settings);
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        ASSERT_TRUE(env.platform_shim->find_in_log(
            "[Vulkan Loader] DEBUG:          Loader Settings Filters for Logging to Standard Error: ERROR | "
            "WARNING | INFO | DEBUG | PERF | DRIVER | LAYER\n"));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_verbose));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_info));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_warning));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_error));
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }
    env.platform_shim->clear_logs();
    env.loader_settings.app_specific_settings.at(0).stderr_log = {"error", "warn", "info", "debug"};
    env.update_loader_settings(env.loader_settings);
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        ASSERT_TRUE(
            env.platform_shim->find_in_log("[Vulkan Loader] DEBUG:          Loader Settings Filters for Logging to Standard "
                                           "Error: ERROR | WARNING | INFO | DEBUG\n"));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_verbose));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_info));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_warning));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_error));
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }
    env.platform_shim->clear_logs();
    env.loader_settings.app_specific_settings.at(0).stderr_log = {"warn", "info", "debug"};
    env.update_loader_settings(env.loader_settings);
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        ASSERT_TRUE(env.platform_shim->find_in_log(
            "[Vulkan Loader] DEBUG:          Loader Settings Filters for Logging to Standard Error: WARNING | INFO | DEBUG\n"));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_verbose));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_info));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_warning));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error));
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }
    env.platform_shim->clear_logs();
    env.loader_settings.app_specific_settings.at(0).stderr_log = {"debug"};
    env.update_loader_settings(env.loader_settings);
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        ASSERT_TRUE(env.platform_shim->find_in_log(
            "[Vulkan Loader] DEBUG:          Loader Settings Filters for Logging to Standard Error: DEBUG\n"));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_verbose));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_info));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_warning));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error));
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }
    env.platform_shim->clear_logs();
    env.loader_settings.app_specific_settings.at(0).stderr_log = {"info"};
    env.update_loader_settings(env.loader_settings);
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        ASSERT_FALSE(env.platform_shim->find_in_log(
            "[Vulkan Loader] DEBUG:          Loader Settings Filters for Logging to Standard Error: INFO\n"));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_verbose));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_info));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_warning));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error));
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }
    env.platform_shim->clear_logs();
    env.loader_settings.app_specific_settings.at(0).stderr_log = {"warn"};
    env.update_loader_settings(env.loader_settings);
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        ASSERT_FALSE(env.platform_shim->find_in_log(
            "[Vulkan Loader] DEBUG:          Loader Settings Filters for Logging to Standard Error: WARNING\n"));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_verbose));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_info));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_warning));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error));
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }
    env.platform_shim->clear_logs();
    env.loader_settings.app_specific_settings.at(0).stderr_log = {"error"};
    env.update_loader_settings(env.loader_settings);
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        ASSERT_FALSE(env.platform_shim->find_in_log(
            "[Vulkan Loader] DEBUG:          Loader Settings Filters for Logging to Standard Error: ERROR\n"));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_verbose));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_info));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_warning));
        ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_error));
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }
    env.platform_shim->clear_logs();
    env.loader_settings.app_specific_settings.at(0).stderr_log = {""};  // Empty string shouldn't be misinterpreted
    env.update_loader_settings(env.loader_settings);
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        ASSERT_FALSE(env.platform_shim->find_in_log(
            "[Vulkan Loader] DEBUG:          Loader Settings Filters for Logging to Standard Error:"));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_verbose));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_info));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_warning));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error));
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }
    env.platform_shim->clear_logs();
    env.loader_settings.app_specific_settings.at(0).stderr_log = {};  // No string in the log
    env.update_loader_settings(env.loader_settings);
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        ASSERT_FALSE(env.platform_shim->find_in_log(
            "[Vulkan Loader] DEBUG:          Loader Settings Filters for Logging to Standard Error:"));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_verbose));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_info));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_warning));
        ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error));
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }
}

// Settings can say which filters to use - make sure the lack of this filter works correctly with VK_LOADER_DEBUG
TEST(SettingsFile, StderrLog_NoOutput) {
    FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("")};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    const char* explicit_layer_name = "Regular_TestLayer1";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name)
                                         .set_path(env.get_shimmed_layer_manifest_path())
                                         .set_control("auto"))
            .add_layer_configuration(
                LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("auto")));
    env.loader_settings.app_specific_settings.at(0).stderr_log = {""};
    env.update_loader_settings(env.loader_settings);

    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty());

        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 0);
        EXPECT_TRUE(active_layer_props.size() == 0);
    }
    // Check if an empty string (vs lack of the stderr_log field) produces correct output
    env.loader_settings.app_specific_settings.at(0).stderr_log = {};
    env.update_loader_settings(env.loader_settings);
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty());
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 0);
        EXPECT_TRUE(active_layer_props.size() == 0);
    }

    env.loader_settings.app_specific_settings.at(0).stderr_log.clear();
    env.update_loader_settings(env.loader_settings);
    {
        EnvVarWrapper instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name};

        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty());

        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }

    {
        EnvVarWrapper instance_layers{"VK_LOADER_LAYERS_ENABLE", explicit_layer_name};

        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty());
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }
    env.loader_settings.app_specific_settings.at(0).stderr_log = {};
    env.update_loader_settings(env.loader_settings);
    {
        EnvVarWrapper instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name};

        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty());

        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }

    {
        EnvVarWrapper instance_layers{"VK_LOADER_LAYERS_ENABLE", explicit_layer_name};

        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();

        EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty());
        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
    }
}

// Settings can say which filters to use - make sure the lack of this filter works correctly with VK_LOADER_DEBUG
TEST(SettingsFile, NoStderr_log_but_VK_LOADER_DEBUG) {
    FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("all")};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* explicit_layer_name = "Regular_TestLayer1";

    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name)
                                         .set_path(env.get_shimmed_layer_manifest_path())
                                         .set_control("auto"))
            .add_layer_configuration(
                LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("auto")));
    env.loader_settings.app_specific_settings.at(0).stderr_log = {};
    env.update_loader_settings(env.loader_settings);

    std::string expected_output_verbose;
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Layer Configurations count = 2\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          ---- Layer Configuration [0] ----\n";
    expected_output_verbose += std::string("[Vulkan Loader] DEBUG:          Name: ") + explicit_layer_name + "\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Path: " + env.get_shimmed_layer_manifest_path().string() + "\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Layer Type: Explicit\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Control: auto\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          ---- Layer Configuration [1] ----\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Name: VK_LAYER_missing\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Path: /road/to/nowhere\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Layer Type: Explicit\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          Control: auto\n";
    expected_output_verbose += "[Vulkan Loader] DEBUG:          ---------------------------------\n";

    std::string expected_output_info =
        std::string("[Vulkan Loader] INFO:           ") + get_settings_location_log_message(env) + "\n";

    std::string expected_output_warning =
        "[Vulkan Loader] WARNING:        Layer name Regular_TestLayer1 does not conform to naming standard (Policy #LLP_LAYER_3)\n";

    std::string expected_output_error =
        "[Vulkan Loader] ERROR:          loader_get_json: Failed to open JSON file /road/to/nowhere\n";

    env.platform_shim->clear_logs();
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        EXPECT_TRUE(env.platform_shim->find_in_log(expected_output_verbose));
        EXPECT_TRUE(env.platform_shim->find_in_log(expected_output_info));
        EXPECT_TRUE(env.platform_shim->find_in_log(expected_output_warning));
        EXPECT_TRUE(env.platform_shim->find_in_log(expected_output_error));

        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 0);
        EXPECT_TRUE(active_layer_props.size() == 0);
    }
}
TEST(SettingsFile, ManyLayersEnabledInManyWays) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    const char* layer1 = "VK_LAYER_layer1";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}.set_name(layer1).set_lib_path(
                                   TEST_LAYER_PATH_EXPORT_VERSION_2)));
    const char* layer2 = "VK_LAYER_layer2";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}.set_name(layer2).set_lib_path(
                                   TEST_LAYER_PATH_EXPORT_VERSION_2)));
    const char* layer3 = "VK_LAYER_layer3";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}.set_name(layer3).set_lib_path(
                                   TEST_LAYER_PATH_EXPORT_VERSION_2)));
    const char* layer4 = "VK_LAYER_layer4";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}.set_name(layer4).set_lib_path(
                                   TEST_LAYER_PATH_EXPORT_VERSION_2)));
    const char* layer5 = "VK_LAYER_layer5";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}.set_name(layer5).set_lib_path(
                                   TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configurations(
            {LoaderSettingsLayerConfiguration{}.set_name(layer1).set_path(env.get_shimmed_layer_manifest_path(0)).set_control("on"),
             LoaderSettingsLayerConfiguration{}
                 .set_name(layer2)
                 .set_path(env.get_shimmed_layer_manifest_path(1))
                 .set_control("auto"),
             LoaderSettingsLayerConfiguration{}
                 .set_name(layer3)
                 .set_path(env.get_shimmed_layer_manifest_path(2))
                 .set_control("auto"),
             LoaderSettingsLayerConfiguration{}
                 .set_name(layer4)
                 .set_path(env.get_shimmed_layer_manifest_path(3))
                 .set_control("auto"),
             LoaderSettingsLayerConfiguration{}
                 .set_name(layer5)
                 .set_path(env.get_shimmed_layer_manifest_path(4))
                 .set_control("on")}));
    env.update_loader_settings(env.loader_settings);

    EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", layer2};
    EnvVarWrapper vk_loader_layers_enable{"VK_LOADER_LAYERS_ENABLE", layer1};

    auto layers = env.GetLayerProperties(5);
    ASSERT_TRUE(string_eq(layers[0].layerName, layer1));
    ASSERT_TRUE(string_eq(layers[1].layerName, layer2));
    ASSERT_TRUE(string_eq(layers[2].layerName, layer3));
    ASSERT_TRUE(string_eq(layers[3].layerName, layer4));
    ASSERT_TRUE(string_eq(layers[4].layerName, layer5));

    InstWrapper inst{env.vulkan_functions};
    inst.CheckCreate();

    auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 3);
    EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, layer1));
    EXPECT_TRUE(string_eq(active_layer_props.at(1).layerName, layer2));
    EXPECT_TRUE(string_eq(active_layer_props.at(2).layerName, layer5));
}

// Enough layers exist that arrays need to be resized - make sure that works
TEST(SettingsFile, TooManyLayers) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});
    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}.add_stderr_log_filter("all"));
    std::string layer_name = "VK_LAYER_regular_layer_name_";
    uint32_t layer_count = 40;
    for (uint32_t i = 0; i < layer_count; i++) {
        env.add_explicit_layer(ManifestOptions{}
                                   .set_json_name(layer_name + std::to_string(i) + ".json")
                                   .set_discovery_type(ManifestDiscoveryType::override_folder),
                               ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(layer_name + std::to_string(i))
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));
        env.loader_settings.app_specific_settings.at(0).add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                                                                    .set_name(layer_name + std::to_string(i))
                                                                                    .set_path(env.get_layer_manifest_path(i))
                                                                                    .set_control("on"));
    }
    env.update_loader_settings(env.loader_settings);

    {
        auto layer_props = env.GetLayerProperties(40);
        for (uint32_t i = 0; i < layer_count; i++) {
            std::string expected_layer_name = layer_name + std::to_string(i);
            EXPECT_TRUE(string_eq(layer_props.at(i).layerName, expected_layer_name.c_str()));
        }
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();

        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        env.debug_log.clear();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 40);
        for (uint32_t i = 0; i < layer_count; i++) {
            std::string expected_layer_name = layer_name + std::to_string(i);
            EXPECT_TRUE(string_eq(layers.at(i).layerName, expected_layer_name.c_str()));
        }
    }
    env.loader_settings.app_specific_settings.at(0).layer_configurations.clear();

    // Now reverse the order to make sure adding the 'last' layer first works
    for (uint32_t i = 0; i < layer_count; i++) {
        env.loader_settings.app_specific_settings.at(0).add_layer_configuration(
            LoaderSettingsLayerConfiguration{}
                .set_name(layer_name + std::to_string(layer_count - i - 1))
                .set_path(env.get_layer_manifest_path(layer_count - i - 1))
                .set_control("on"));
    }
    env.update_loader_settings(env.loader_settings);

    {
        auto layer_props = env.GetLayerProperties(40);
        for (uint32_t i = 0; i < layer_count; i++) {
            std::string expected_layer_name = layer_name + std::to_string(layer_count - i - 1);
            EXPECT_TRUE(string_eq(layer_props.at(i).layerName, expected_layer_name.c_str()));
        }
        InstWrapper inst{env.vulkan_functions};
        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
        inst.CheckCreate();

        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
        env.debug_log.clear();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 40);
        for (uint32_t i = 0; i < layer_count; i++) {
            std::string expected_layer_name = layer_name + std::to_string(layer_count - i - 1);
            EXPECT_TRUE(string_eq(layers.at(i).layerName, expected_layer_name.c_str()));
        }
    }
}

TEST(SettingsFile, EnvVarsWorkTogether) {
    FrameworkEnvironment env{};
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device(PhysicalDevice{}.set_deviceName("regular").finish());
    env.add_icd(TEST_ICD_PATH_VERSION_2, ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::env_var))
        .add_physical_device(PhysicalDevice{}.set_deviceName("env_var").finish());
    env.add_icd(TEST_ICD_PATH_VERSION_2, ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::add_env_var))
        .add_physical_device(PhysicalDevice{}.set_deviceName("add_env_var").finish());

    const char* regular_explicit_layer = "VK_LAYER_regular_explicit_layer";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(regular_explicit_layer).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* regular_explicit_layer_settings_file_set_on = "VK_LAYER_regular_explicit_layer_settings_file_set_on";
    env.add_explicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_name(regular_explicit_layer_settings_file_set_on)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* env_var_explicit_layer = "VK_LAYER_env_var_explicit_layer";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::env_var),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(env_var_explicit_layer).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* add_env_var_explicit_layer = "VK_LAYER_add_env_var_explicit_layer";
    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::add_env_var),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(add_env_var_explicit_layer).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* regular_implicit_layer = "VK_LAYER_regular_implicit_layer";
    env.add_implicit_layer({}, ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                             .set_disable_environment("A")
                                                             .set_name(regular_implicit_layer)
                                                             .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* env_var_implicit_layer = "VK_LAYER_env_var_implicit_layer";
    env.add_implicit_layer(ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::env_var),
                           ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                         .set_disable_environment("B")
                                                         .set_name(env_var_implicit_layer)
                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    const char* add_env_var_implicit_layer = "VK_LAYER_add_env_var_implicit_layer";
    env.add_implicit_layer(ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::add_env_var),
                           ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                         .set_disable_environment("C")
                                                         .set_name(add_env_var_implicit_layer)
                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    // Settings file only contains the one layer it wants enabled
    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(regular_implicit_layer)
                                         .set_path(env.get_shimmed_layer_manifest_path(4))
                                         .set_control("auto")
                                         .set_treat_as_implicit_manifest(true))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(regular_explicit_layer)
                                         .set_path(env.get_shimmed_layer_manifest_path(0))
                                         .set_control("auto"))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(regular_explicit_layer_settings_file_set_on)
                                         .set_path(env.get_shimmed_layer_manifest_path(1))
                                         .set_control("on"))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location")));
    env.update_loader_settings(env.loader_settings);

    {  // VK_INSTANCE_LAYERS
        EnvVarWrapper instance_layers{"VK_INSTANCE_LAYERS", regular_explicit_layer};
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4);
        EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer));
        EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer));
        EXPECT_TRUE(string_eq(layers.at(2).layerName, regular_explicit_layer_settings_file_set_on));
        EXPECT_TRUE(string_eq(layers.at(3).layerName, env_var_implicit_layer));
        EXPECT_TRUE(env.platform_shim->find_in_log(
            "env var 'VK_INSTANCE_LAYERS' defined and adding layers: VK_LAYER_regular_explicit_layer"));
    }
    env.platform_shim->clear_logs();
    {  // VK_LOADER_LAYERS_ENABLE
        EnvVarWrapper env_var_enable{"VK_LOADER_LAYERS_ENABLE", regular_explicit_layer};
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4);
        EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer));
        EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer));
        EXPECT_TRUE(string_eq(layers.at(2).layerName, regular_explicit_layer_settings_file_set_on));
        EXPECT_TRUE(string_eq(layers.at(3).layerName, env_var_implicit_layer));
        EXPECT_TRUE(env.platform_shim->find_in_log(
            "Layer \"VK_LAYER_regular_explicit_layer\" forced enabled due to env var 'VK_LOADER_LAYERS_ENABLE'"));
    }
    env.platform_shim->clear_logs();
    {                                                                        // VK_LOADER_LAYERS_DISABLE
        EnvVarWrapper env_var_disable{"VK_LOADER_LAYERS_DISABLE", "~all~"};  // ignored by settings file
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
        EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_explicit_layer_settings_file_set_on));
        EXPECT_TRUE(
            env.platform_shim->find_in_log("Layer \"VK_LAYER_env_var_implicit_layer\" forced disabled because name matches filter "
                                           "of env var 'VK_LOADER_LAYERS_DISABLE'"));
    }
    env.platform_shim->clear_logs();

    {  // VK_LOADER_LAYERS_ALLOW
        EnvVarWrapper env_var_allow{"VK_LOADER_LAYERS_ALLOW", regular_implicit_layer};
        // Allow only makes sense when the disable env-var is also set
        EnvVarWrapper env_var_disable{"VK_LOADER_LAYERS_DISABLE", "~implicit~"};

        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
        // The regular_implicit_layer is set to "auto" so is affected by environment variables
        EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer));
        EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer_settings_file_set_on));

        EXPECT_TRUE(
            env.platform_shim->find_in_log("Layer \"VK_LAYER_env_var_implicit_layer\" forced disabled because name matches filter "
                                           "of env var 'VK_LOADER_LAYERS_DISABLE'"));
    }
    env.platform_shim->clear_logs();

    {  // VK_LAYER_PATH
        // VK_LAYER_PATH is set by add_explicit_layer()
        InstWrapper inst{env.vulkan_functions};
        inst.create_info.add_layer(env_var_explicit_layer);
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4);
        EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer));
        EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer_settings_file_set_on));
        EXPECT_TRUE(string_eq(layers.at(2).layerName, env_var_implicit_layer));
        EXPECT_TRUE(string_eq(layers.at(3).layerName, env_var_explicit_layer));
        EXPECT_TRUE(env.platform_shim->find_in_log("Insert instance layer \"VK_LAYER_env_var_explicit_layer\""));
    }
    env.platform_shim->clear_logs();
    {  // VK_IMPLICIT_LAYER_PATH
        // VK_IMPLICIT_LAYER_PATH is set by add_implicit_layer()
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer));
        EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer_settings_file_set_on));
        EXPECT_TRUE(string_eq(layers.at(2).layerName, env_var_implicit_layer));
        EXPECT_TRUE(env.platform_shim->find_in_log("Insert instance layer \"VK_LAYER_env_var_implicit_layer\""));
    }
    env.platform_shim->clear_logs();
    {  // VK_ADD_LAYER_PATH
        // VK_ADD_LAYER_PATH is set by add_explicit_layer(), but we need to disable VK_LAYER_PATH
        // since VK_LAYER_PATH overrides VK_ADD_LAYER_PATH
        EnvVarWrapper env_var_vk_layer_path{"VK_LAYER_PATH", ""};
        env_var_vk_layer_path.remove_value();
        InstWrapper inst{env.vulkan_functions};
        inst.create_info.add_layer(add_env_var_explicit_layer);
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4);
        EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer));
        EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer_settings_file_set_on));
        EXPECT_TRUE(string_eq(layers.at(2).layerName, env_var_implicit_layer));
        EXPECT_TRUE(string_eq(layers.at(3).layerName, add_env_var_explicit_layer));
        EXPECT_TRUE(env.platform_shim->find_in_log("Insert instance layer \"VK_LAYER_add_env_var_explicit_layer\""));
    }
    env.platform_shim->clear_logs();
    {  // VK_ADD_IMPLICIT_LAYER_PATH
        // VK_ADD_IMPLICIT_LAYER_PATH is set by add_explicit_layer(), but we need to disable VK_LAYER_PATH
        // since VK_IMPLICIT_LAYER_PATH overrides VK_ADD_IMPLICIT_LAYER_PATH
        EnvVarWrapper env_var_vk_layer_path{"VK_IMPLICIT_LAYER_PATH", ""};
        env_var_vk_layer_path.remove_value();
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
        EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer));
        EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer_settings_file_set_on));
        EXPECT_TRUE(string_eq(layers.at(2).layerName, add_env_var_implicit_layer));
        EXPECT_TRUE(env.platform_shim->find_in_log("Insert instance layer \"VK_LAYER_add_env_var_implicit_layer\""));
    }
}

TEST(SettingsFile, DontAllowDuplicatesBetweenSettingsLayersAndDefaultLayers) {
    FrameworkEnvironment env;
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device({});

    const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_stderr_log_filter("all")
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"))
            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
                                         .set_name(explicit_layer_name1)
                                         .set_path(env.get_shimmed_layer_manifest_path(1))
                                         .set_control("on"))));

    auto layer_props = env.GetLayerProperties(1);
    ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1));

    InstWrapper inst{env.vulkan_functions};
    inst.CheckCreate();

    auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
    ASSERT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name1));
}

// additional drivers being provided by settings file
TEST(SettingsFile, AdditionalDrivers) {
    FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("")};
    const char* regular_driver_name = "regular";
    const char* settings_driver_name = "settings";
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device(PhysicalDevice{}.set_deviceName(regular_driver_name).finish());
    env.add_icd(TEST_ICD_PATH_VERSION_2, ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder))
        .add_physical_device(PhysicalDevice{}.set_deviceName(settings_driver_name).finish());

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}.add_stderr_log_filter("all").add_driver_configuration(
            LoaderSettingsDriverConfiguration{}.set_path(env.get_icd_manifest_path(1))));
    env.update_loader_settings(env.loader_settings);

    InstWrapper inst{env.vulkan_functions};
    inst.CheckCreate();
    auto pds = inst.GetPhysDevs();
    ASSERT_EQ(pds.size(), 2U);
    VkPhysicalDeviceProperties props1{}, props2{};
    inst.functions->vkGetPhysicalDeviceProperties(pds.at(0), &props1);
    inst.functions->vkGetPhysicalDeviceProperties(pds.at(1), &props2);
    ASSERT_TRUE(string_eq(props1.deviceName, regular_driver_name));
    ASSERT_TRUE(string_eq(props2.deviceName, settings_driver_name));
}
// settings file provided drivers replacing system found drivers
TEST(SettingsFile, ExclusiveAdditionalDrivers) {
    FrameworkEnvironment env{};
    const char* regular_driver_name = "regular";
    const char* settings_driver_name = "settings";
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device(PhysicalDevice{}.set_deviceName(regular_driver_name).finish());
    env.add_icd(TEST_ICD_PATH_VERSION_2, ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder))
        .add_physical_device(PhysicalDevice{}.set_deviceName(settings_driver_name).finish());

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}.set_additional_drivers_use_exclusively(true).add_driver_configuration(
            LoaderSettingsDriverConfiguration{}.set_path(env.get_icd_manifest_path(1))));
    env.update_loader_settings(env.loader_settings);

    InstWrapper inst{env.vulkan_functions};
    inst.CheckCreate();
    auto pds = inst.GetPhysDevs();
    ASSERT_EQ(pds.size(), 1U);
    VkPhysicalDeviceProperties props1{};
    inst.functions->vkGetPhysicalDeviceProperties(pds.at(0), &props1);
    ASSERT_TRUE(string_eq(props1.deviceName, settings_driver_name));
}
// settings file provided drivers + VK_LOADER_DRIVERS_SELECT
TEST(SettingsFile, AdditionalDriversReplacesVK_LOADER_DRIVERS_SELECT) {
    FrameworkEnvironment env{};
    const char* regular_driver_name = "regular";
    const char* settings_driver_name = "settings";
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device(PhysicalDevice{}.set_deviceName(regular_driver_name).finish());
    env.add_icd(TEST_ICD_PATH_VERSION_2, ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder))
        .add_physical_device(PhysicalDevice{}.set_deviceName(settings_driver_name).finish());

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}.add_driver_configuration(LoaderSettingsDriverConfiguration{}.set_path(env.get_icd_manifest_path(1))));
    env.update_loader_settings(env.loader_settings);

    {
        EnvVarWrapper VK_LOADER_DRIVERS_SELECT{"VK_LOADER_DRIVERS_SELECT",
                                               std::string("*") + env.get_icd_manifest_path(0).stem().string() + std::string("*")};
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto pds = inst.GetPhysDevs();
        ASSERT_EQ(pds.size(), 1U);
        VkPhysicalDeviceProperties props1{};
        inst.functions->vkGetPhysicalDeviceProperties(pds.at(0), &props1);
        ASSERT_TRUE(string_eq(props1.deviceName, regular_driver_name));
    }
    {
        EnvVarWrapper VK_LOADER_DRIVERS_SELECT{"VK_LOADER_DRIVERS_SELECT",
                                               std::string("*") + env.get_icd_manifest_path(1).stem().string() + std::string("*")};
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto pds = inst.GetPhysDevs();
        ASSERT_EQ(pds.size(), 1U);
        VkPhysicalDeviceProperties props1{};
        inst.functions->vkGetPhysicalDeviceProperties(pds.at(0), &props1);
        ASSERT_TRUE(string_eq(props1.deviceName, settings_driver_name));
    }
    env.loader_settings.app_specific_settings.at(0).set_additional_drivers_use_exclusively(true);
    env.update_loader_settings(env.loader_settings);
    {
        EnvVarWrapper VK_LOADER_DRIVERS_SELECT{"VK_LOADER_DRIVERS_SELECT",
                                               std::string("*") + env.get_icd_manifest_path(0).stem().string() + std::string("*")};
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER);
    }
    {
        EnvVarWrapper VK_LOADER_DRIVERS_SELECT{"VK_LOADER_DRIVERS_SELECT",
                                               std::string("*") + env.get_icd_manifest_path(1).stem().string() + std::string("*")};
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto pds = inst.GetPhysDevs();
        ASSERT_EQ(pds.size(), 1U);
        VkPhysicalDeviceProperties props1{};
        inst.functions->vkGetPhysicalDeviceProperties(pds.at(0), &props1);
        ASSERT_TRUE(string_eq(props1.deviceName, settings_driver_name));
    }
}

// settings file provided drivers + VK_LOADER_DRIVERS_DISABLE
TEST(SettingsFile, AdditionalDriversReplacesVK_LOADER_DRIVERS_DISABLE) {
    // TODO
}

TEST(SettingsFile, InvalidAdditionalDriversField) {
    FrameworkEnvironment env{};
    const char* layer_name = "VK_LAYER_layer";
    const char* driver_name = "driver";
    const char* settings_driver_name = "settings_driver";
    env.add_icd(TEST_ICD_PATH_VERSION_2, ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder))
        .add_physical_device(PhysicalDevice{}.set_deviceName(settings_driver_name).finish());
    env.add_icd(TEST_ICD_PATH_VERSION_2).add_physical_device(PhysicalDevice{}.set_deviceName(driver_name).finish());

    env.add_explicit_layer(
        ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder),
        ManifestLayer{}.add_layer(
            ManifestLayer::LayerDescription{}.set_name("VK_LAYER_layer").set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    JsonWriter writer;
    writer.StartObject();
    writer.AddKeyedString("file_format_version", "1.0.0");

    writer.StartKeyedObject("settings");
    writer.StartKeyedArray("additional_drivers");
    writer.AddString(env.get_shimmed_icd_manifest_path(0));
    writer.EndArray();
    writer.StartKeyedArray("layers");
    writer.StartObject();
    writer.AddKeyedString("name", layer_name);
    writer.AddKeyedString("path", env.get_shimmed_layer_manifest_path(0));
    writer.AddKeyedString("control", "on");
    writer.EndObject();
    writer.EndArray();
    writer.EndObject();
    writer.EndObject();
    env.write_settings_file(writer.output, true);

    InstWrapper inst{env.vulkan_functions};
    inst.CheckCreate();
    auto pd = inst.GetPhysDev();
    VkPhysicalDeviceProperties props1{};
    inst.functions->vkGetPhysicalDeviceProperties(pd, &props1);
    ASSERT_TRUE(string_eq(props1.deviceName, driver_name));

    auto active_layer_props = inst.GetActiveLayers(pd, 1);
    EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, layer_name));
}

TEST(SettingsFile, DriverConfigurationsInSpecifiedOrder) {
    FrameworkEnvironment env{};
    std::vector<VulkanUUID> uuids{10, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}};

    // Mix up the uuid's so that they are all unique
    int count = 1;
    for (auto& uuid : uuids) {
        std::rotate(uuid.begin(), uuid.begin() + count, uuid.end());
        count++;
    }

    auto& icd = env.add_icd(TEST_ICD_PATH_VERSION_2).set_icd_api_version(VK_API_VERSION_1_1);
    for (uint32_t i = 0; i < uuids.size(); i++) {
        // add the physical devices in reverse order of UUID's
        icd.add_physical_device(PhysicalDevice()
                                    .set_deviceName("PhysicalDevice_" + std::to_string(uuids.size() - 1 - i))
                                    .set_api_version(VK_API_VERSION_1_1)
                                    .set_deviceUUID(uuids[uuids.size() - 1 - i])
                                    .finish());
    }

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(AppSpecificSettings{});
    for (uint32_t i = 0; i < uuids.size(); i++) {
        env.loader_settings.app_specific_settings.at(0).add_device_configuration(
            LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[i]));
    }
    env.update_loader_settings(env.loader_settings);

    InstWrapper inst{env.vulkan_functions};
    inst.CheckCreate();
    auto pds = inst.GetPhysDevs();
    ASSERT_EQ(pds.size(), uuids.size());
    for (uint32_t i = 0; i < uuids.size(); i++) {
        VkPhysicalDeviceProperties props{};

        inst->vkGetPhysicalDeviceProperties(pds.at(i), &props);
        std::string s = "PhysicalDevice_" + std::to_string(i);
        ASSERT_TRUE(string_eq(s.c_str(), props.deviceName));
    }
}

TEST(SettingsFile, OnlyOneDriverConfiguration) {
    FrameworkEnvironment env{};
    std::vector<VulkanUUID> uuids{10, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}};

    // Mix up the uuid's so that they are all unique
    int count = 1;
    for (auto& uuid : uuids) {
        std::rotate(uuid.begin(), uuid.begin() + count, uuid.end());
        count++;
    }

    auto& icd = env.add_icd(TEST_ICD_PATH_VERSION_2).set_icd_api_version(VK_API_VERSION_1_1);
    for (uint32_t i = 0; i < uuids.size(); i++) {
        // add the physical devices in reverse order of UUID's
        icd.add_physical_device(
            PhysicalDevice().set_api_version(VK_API_VERSION_1_1).set_deviceUUID(uuids[uuids.size() - 1 - i]).finish());
    }

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(AppSpecificSettings{});

    for (uint32_t i = 0; i < uuids.size(); i++) {
        env.loader_settings.app_specific_settings.at(0).device_configurations.clear();
        env.loader_settings.app_specific_settings.at(0).add_device_configuration(
            LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[i]));

        env.update_loader_settings(env.loader_settings);

        InstWrapper inst{env.vulkan_functions};
        inst.create_info.set_api_version(VK_API_VERSION_1_1);
        inst.CheckCreate();
        auto pd = inst.GetPhysDev();

        VkPhysicalDeviceVulkan11Properties vulkan_11_props{};
        vulkan_11_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;
        VkPhysicalDeviceProperties2 props2{};
        props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
        props2.pNext = &vulkan_11_props;
        inst->vkGetPhysicalDeviceProperties2(pd, &props2);

        ASSERT_TRUE(0 == memcmp(vulkan_11_props.deviceUUID, uuids[i].data(), VK_UUID_SIZE * sizeof(uint8_t)));
    }
}

TEST(SettingsFile, MissingDriverConfiguration) {
    FrameworkEnvironment env{};
    std::vector<VulkanUUID> uuids{2, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}};

    // Mix up the uuid's so that they are all unique
    int count = 1;
    for (auto& uuid : uuids) {
        std::rotate(uuid.begin(), uuid.begin() + count, uuid.end());
        count++;
    }

    auto& icd = env.add_icd(TEST_ICD_PATH_VERSION_2).set_icd_api_version(VK_API_VERSION_1_1);
    icd.add_physical_device(PhysicalDevice().set_api_version(VK_API_VERSION_1_1).set_deviceUUID(uuids[1]).finish());

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(AppSpecificSettings{});

    env.loader_settings.app_specific_settings.at(0).device_configurations.clear();
    env.loader_settings.app_specific_settings.at(0).add_device_configuration(
        LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[0]));

    env.update_loader_settings(env.loader_settings);

    InstWrapper inst{env.vulkan_functions};
    inst.CheckCreate();
    inst.GetPhysDev(VK_ERROR_INITIALIZATION_FAILED);
}

TEST(SettingsFile, DeviceConfigurationWithSameDriver) {
    FrameworkEnvironment env{};
    VulkanUUID device_uuid = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
    VulkanUUID driver_uuid = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25};

    auto& icd0 = env.add_icd(TEST_ICD_PATH_VERSION_2).set_icd_api_version(VK_API_VERSION_1_1);
    auto& phys_dev_0 = icd0.add_and_get_physical_device(PhysicalDevice()
                                                            .set_api_version(VK_API_VERSION_1_2)
                                                            .set_deviceUUID(device_uuid)
                                                            .set_driverUUID(driver_uuid)
                                                            .set_deviceName("foobar")
                                                            .finish());
    phys_dev_0.properties.driverVersion = 1000;
    std::string("Fake Driver XYZ").copy(phys_dev_0.driver_properties.driverName, VK_MAX_EXTENSION_NAME_SIZE);

    auto& icd1 = env.add_icd(TEST_ICD_PATH_VERSION_2).set_icd_api_version(VK_API_VERSION_1_1);
    auto& phys_dev_1 = icd1.add_and_get_physical_device(PhysicalDevice()
                                                            .set_api_version(VK_API_VERSION_1_2)
                                                            .set_deviceUUID(device_uuid)
                                                            .set_driverUUID(driver_uuid)
                                                            .set_deviceName("foobar")
                                                            .finish());
    phys_dev_1.properties.driverVersion = 30;
    std::string("Fake Driver XYZ, but differently named").copy(phys_dev_1.driver_properties.driverName, VK_MAX_EXTENSION_NAME_SIZE);

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(AppSpecificSettings{});

    env.loader_settings.app_specific_settings.at(0).device_configurations.clear();
    env.loader_settings.app_specific_settings.at(0).add_device_configuration(
        LoaderSettingsDeviceConfiguration{}
            .set_deviceUUID(device_uuid)
            .set_driverUUID(driver_uuid)
            .set_driverVersion(phys_dev_1.properties.driverVersion));
    env.loader_settings.app_specific_settings.at(0).add_device_configuration(
        LoaderSettingsDeviceConfiguration{}
            .set_deviceUUID(device_uuid)
            .set_driverUUID(driver_uuid)
            .set_driverVersion(phys_dev_0.properties.driverVersion));
    env.update_loader_settings(env.loader_settings);

    InstWrapper inst{env.vulkan_functions};
    inst.CheckCreate();
    auto phys_devs = inst.GetPhysDevs(2);
    VkPhysicalDeviceProperties props1{};
    VkPhysicalDeviceProperties props2{};
    inst->vkGetPhysicalDeviceProperties(phys_devs.at(0), &props1);
    inst->vkGetPhysicalDeviceProperties(phys_devs.at(1), &props2);
    ASSERT_EQ(props1.driverVersion, phys_dev_1.properties.driverVersion);
    ASSERT_EQ(props2.driverVersion, phys_dev_0.properties.driverVersion);
}

// Three drivers, second on has the matching UUID in the settings file.
TEST(SettingsFile, DriverConfigurationIgnoresDriverEnvVars) {
    FrameworkEnvironment env{};
    std::vector<VulkanUUID> uuids{3, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}};

    // Mix up the uuid's so that they are all unique
    int count = 1;
    for (auto& uuid : uuids) {
        std::rotate(uuid.begin(), uuid.begin() + count, uuid.end());
        count++;
    }

    env.add_icd(TEST_ICD_PATH_VERSION_2)
        .add_physical_device(PhysicalDevice().set_deviceName("A").set_deviceUUID(uuids[0]).finish());

    auto& icd = env.add_icd(TEST_ICD_PATH_VERSION_2).set_icd_api_version(VK_API_VERSION_1_1);
    icd.add_physical_device(
        PhysicalDevice().set_api_version(VK_API_VERSION_1_1).set_deviceName("B").set_deviceUUID(uuids[1]).finish());

    env.add_icd(TEST_ICD_PATH_VERSION_2, ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::env_var))
        .add_physical_device(PhysicalDevice().set_deviceName("C").set_deviceUUID(uuids[2]).finish());

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(AppSpecificSettings{});

    env.loader_settings.app_specific_settings.at(0).device_configurations.clear();
    env.loader_settings.app_specific_settings.at(0).add_device_configuration(
        LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[1]));

    env.update_loader_settings(env.loader_settings);

    InstWrapper inst{env.vulkan_functions};
    inst.create_info.set_api_version(VK_API_VERSION_1_1);
    inst.CheckCreate();
    auto pd = inst.GetPhysDev();

    VkPhysicalDeviceVulkan11Properties vulkan_11_props{};
    vulkan_11_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;
    VkPhysicalDeviceProperties2 props2{};
    props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
    props2.pNext = &vulkan_11_props;
    inst->vkGetPhysicalDeviceProperties2(pd, &props2);

    ASSERT_TRUE(0 == memcmp(vulkan_11_props.deviceUUID, uuids[1].data(), VK_UUID_SIZE * sizeof(uint8_t)));
}

TEST(SettingsFile, DriverConfigurationsAndAdditionalDrivers) {
    FrameworkEnvironment env{};

    std::vector<VulkanUUID> uuids{3, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}};
    // Mix up the uuid's so that they are all unique
    int count = 1;
    for (auto& uuid : uuids) {
        std::rotate(uuid.begin(), uuid.begin() + count, uuid.end());
        count++;
    }

    env.add_icd(TEST_ICD_PATH_VERSION_2, ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder))
        .add_physical_device(PhysicalDevice{}
                                 .set_api_version(VK_API_VERSION_1_1)
                                 .set_deviceName("additional_device")
                                 .set_deviceUUID(uuids[0])
                                 .finish());

    auto& icd = env.add_icd(TEST_ICD_PATH_VERSION_2)
                    .set_icd_api_version(VK_API_VERSION_1_1)
                    .add_physical_device(PhysicalDevice()
                                             .set_api_version(VK_API_VERSION_1_1)
                                             .set_deviceName("device_configuration_device")
                                             .set_deviceUUID(uuids[1])
                                             .finish());
    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(AppSpecificSettings{});
    env.loader_settings.app_specific_settings.at(0).add_driver_configuration(
        LoaderSettingsDriverConfiguration().set_path(env.get_icd_manifest_path(0)));
    env.loader_settings.app_specific_settings.at(0).add_device_configuration(
        LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[1]));
    env.update_loader_settings(env.loader_settings);

    InstWrapper inst{env.vulkan_functions};
    inst.create_info.set_api_version(VK_API_VERSION_1_1);
    inst.CheckCreate();
    auto pd = inst.GetPhysDev();
    VkPhysicalDeviceProperties props{};
    inst->vkGetPhysicalDeviceProperties(pd, &props);
    ASSERT_TRUE(string_eq("device_configuration_device", props.deviceName));
    VkPhysicalDeviceVulkan11Properties vulkan_11_props{};
    vulkan_11_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;
    VkPhysicalDeviceProperties2 props2{};
    props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
    props2.pNext = &vulkan_11_props;
    inst->vkGetPhysicalDeviceProperties2(pd, &props2);

    ASSERT_TRUE(0 == memcmp(vulkan_11_props.deviceUUID, uuids[1].data(), VK_UUID_SIZE * sizeof(uint8_t)));
}

TEST(SettingsFile, InvalidDriverConfigurations) {
    FrameworkEnvironment env{};
    std::vector<VulkanUUID> uuids{3, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}};

    // Mix up the uuid's so that they are all unique
    int count = 1;
    for (auto& uuid : uuids) {
        std::rotate(uuid.begin(), uuid.begin() + count, uuid.end());
        count++;
    }
    env.add_icd(TEST_ICD_PATH_VERSION_2)
        .set_icd_api_version(VK_API_VERSION_1_1)
        .add_physical_device(PhysicalDevice{}
                                 .set_api_version(VK_API_VERSION_1_1)
                                 .set_deviceName("regular_driver")
                                 .set_deviceUUID(uuids[0])
                                 .finish());

    env.add_icd(TEST_ICD_PATH_VERSION_2, ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder))
        .add_physical_device(PhysicalDevice{}
                                 .set_api_version(VK_API_VERSION_1_1)
                                 .set_deviceName("additional_device")
                                 .set_deviceUUID(uuids[2])
                                 .finish());

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .set_additional_drivers_use_exclusively(true)
            .add_driver_configuration(LoaderSettingsDriverConfiguration{}.set_path(env.get_icd_manifest_path(1)))
            .add_device_configuration(LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[1])));
    env.update_loader_settings(env.loader_settings);

    InstWrapper inst{env.vulkan_functions};
    inst.CheckCreate();
    auto pd = inst.GetPhysDev(VK_ERROR_INITIALIZATION_FAILED);
}

TEST(SettingsFile, DeviceConfigurationReordersAdditionalDrivers) {
    FrameworkEnvironment env{};
    std::vector<VulkanUUID> uuids{3, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}};

    // Mix up the uuid's so that they are all unique
    int count = 1;
    for (auto& uuid : uuids) {
        std::rotate(uuid.begin(), uuid.begin() + count, uuid.end());
        count++;
    }

    env.add_icd(TEST_ICD_PATH_VERSION_2)
        .set_icd_api_version(VK_API_VERSION_1_1)
        .add_physical_device(PhysicalDevice{}
                                 .set_api_version(VK_API_VERSION_1_1)
                                 .set_deviceName("regular_driverA")
                                 .set_deviceUUID(uuids[0])
                                 .finish());
    env.add_icd(TEST_ICD_PATH_VERSION_2)
        .set_icd_api_version(VK_API_VERSION_1_1)
        .add_physical_device(PhysicalDevice{}
                                 .set_api_version(VK_API_VERSION_1_1)
                                 .set_deviceName("regular_driverB")
                                 .set_deviceUUID(uuids[1])
                                 .finish());

    env.add_icd(TEST_ICD_PATH_VERSION_2, ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder))
        .set_icd_api_version(VK_API_VERSION_1_1)
        .add_physical_device(PhysicalDevice{}
                                 .set_api_version(VK_API_VERSION_1_1)
                                 .set_deviceName("additional_device")
                                 .set_deviceUUID(uuids[2])
                                 .finish());

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_driver_configuration(LoaderSettingsDriverConfiguration{}.set_path(env.get_icd_manifest_path(2)))
            .add_device_configuration(LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[2]))
            .add_device_configuration(LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[0]))
            .add_device_configuration(LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[1])));
    env.update_loader_settings(env.loader_settings);
    {
        InstWrapper inst{env.vulkan_functions};
        inst.CheckCreate();
        auto pd = inst.GetPhysDevs(3).at(0);
        VkPhysicalDeviceProperties props{};
        env.vulkan_functions.vkGetPhysicalDeviceProperties(pd, &props);
        ASSERT_TRUE(string_eq(props.deviceName, "additional_device"));
    }
    {  // do the same check but with 1.1 so we can check that the UUID matches
        InstWrapper inst{env.vulkan_functions};
        inst.create_info.set_api_version(VK_API_VERSION_1_1);
        inst.CheckCreate();
        auto pd = inst.GetPhysDevs(3).at(0);
        VkPhysicalDeviceProperties props{};
        env.vulkan_functions.vkGetPhysicalDeviceProperties(pd, &props);
        ASSERT_TRUE(string_eq(props.deviceName, "additional_device"));

        VkPhysicalDeviceVulkan11Properties vulkan_11_props{};
        vulkan_11_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;
        VkPhysicalDeviceProperties2 props2{};
        props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
        props2.pNext = &vulkan_11_props;
        inst->vkGetPhysicalDeviceProperties2(pd, &props2);

        ASSERT_TRUE(0 == memcmp(vulkan_11_props.deviceUUID, uuids[2].data(), VK_UUID_SIZE * sizeof(uint8_t)));
    }
}

TEST(SettingsFile, DeviceConfigurationReordersExclusiveAdditionalDrivers) {
    FrameworkEnvironment env{};
    std::vector<VulkanUUID> uuids{3, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}};

    // Mix up the uuid's so that they are all unique
    int count = 1;
    for (auto& uuid : uuids) {
        std::rotate(uuid.begin(), uuid.begin() + count, uuid.end());
        count++;
    }

    env.add_icd(TEST_ICD_PATH_VERSION_2)
        .set_icd_api_version(VK_API_VERSION_1_1)
        .add_physical_device(PhysicalDevice{}
                                 .set_api_version(VK_API_VERSION_1_1)
                                 .set_deviceName("regular_driverA")
                                 .set_deviceUUID(uuids[0])
                                 .finish());
    env.add_icd(TEST_ICD_PATH_VERSION_2)
        .set_icd_api_version(VK_API_VERSION_1_1)
        .add_physical_device(PhysicalDevice{}
                                 .set_api_version(VK_API_VERSION_1_1)
                                 .set_deviceName("regular_driverB")
                                 .set_deviceUUID(uuids[1])
                                 .finish());

    env.add_icd(TEST_ICD_PATH_VERSION_2, ManifestOptions{}.set_discovery_type(ManifestDiscoveryType::override_folder))
        .set_icd_api_version(VK_API_VERSION_1_1)
        .add_physical_device(PhysicalDevice{}
                                 .set_api_version(VK_API_VERSION_1_1)
                                 .set_deviceName("additional_device")
                                 .set_deviceUUID(uuids[2])
                                 .finish());

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .set_additional_drivers_use_exclusively(true)
            .add_driver_configuration(LoaderSettingsDriverConfiguration{}.set_path(env.get_icd_manifest_path(2)))
            .add_device_configuration(LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[2])));
    env.update_loader_settings(env.loader_settings);

    InstWrapper inst{env.vulkan_functions};
    inst.create_info.set_api_version(VK_API_VERSION_1_1);
    inst.CheckCreate();
    auto pd = inst.GetPhysDev();
    VkPhysicalDeviceProperties props{};
    env.vulkan_functions.vkGetPhysicalDeviceProperties(pd, &props);
    ASSERT_TRUE(string_eq(props.deviceName, "additional_device"));

    VkPhysicalDeviceVulkan11Properties vulkan_11_props{};
    vulkan_11_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;
    VkPhysicalDeviceProperties2 props2{};
    props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
    props2.pNext = &vulkan_11_props;
    inst->vkGetPhysicalDeviceProperties2(pd, &props2);

    ASSERT_TRUE(0 == memcmp(vulkan_11_props.deviceUUID, uuids[2].data(), VK_UUID_SIZE * sizeof(uint8_t)));
}
TEST(SettingsFile, DeviceConfigurationPreservesLayerEnumeration) {
    FrameworkEnvironment env{};
    VulkanUUID uuid{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

    const char* layer_name = "VK_LAYER_layer";
    env.add_explicit_layer(
        {}, ManifestLayer{}.add_layer(
                ManifestLayer::LayerDescription{}.set_name(layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)));

    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
        AppSpecificSettings{}
            .add_driver_configuration(LoaderSettingsDriverConfiguration{}.set_path("Shouldn't matter"))
            .add_device_configuration(LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuid)));
    env.update_loader_settings(env.loader_settings);

    auto layer_props = env.GetLayerProperties(1);
    EXPECT_TRUE(string_eq(layer_props.at(0).layerName, layer_name));
}
