/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include #include "Common/ShaderBuilderTestFixture.h" namespace UnitTest { using namespace AZ; struct KeyValueView { AZStd::string_view m_key; AZStd::string_view m_value; }; class SupervariantCmdArgumentTests : public ShaderBuilderTestFixture { protected: static constexpr char MCPP_MACRO1[] = "MACRO1"; static constexpr char MCPP_VALUE1[] = "VALUE1a"; static constexpr char MCPP_NEW_VALUE1[] = "VALUE1b"; // Missing A is not a typo static constexpr char MCPP_MACRO2[] = "MACRO2"; static constexpr char MCPP_VALUE2[] = "VALUE2"; static constexpr char MCPP_MACRO3[] = "MACRO3"; static constexpr char MCPP_VALUE3[] = "VALUE3a"; static constexpr char MCPP_NEW_VALUE3[] = "VALUE3b"; static constexpr char MCPP_MACRO4[] = "MACRO4"; static constexpr char MCPP_MACRO5[] = "MACRO5"; static constexpr char MCPP_MACRO6[] = "MACRO6"; static constexpr char MCPP_VALUE6[] = "VALUE6"; static constexpr char AZSLC_ARG1[] = "--azsl1"; static constexpr char AZSLC_ARG2[] = "--azsl2"; static constexpr char AZSLC_VAL2[] = "open,source"; static constexpr char AZSLC_NEW_VAL2a[] = "closed,binary"; static constexpr char AZSLC_NEW_VAL2b[] = "closed,source"; static constexpr char AZSLC_ARG3[] = "--azsl3"; static constexpr char AZSLC_VAL3[] = "blue"; static constexpr char AZSLC_ARG4[] = "-azsl4"; static constexpr char AZSLC_ARG5[] = "--azsl5"; static constexpr char AZSLC_VAL5[] = "smith,wick,john,45,-1,-1"; static constexpr char AZSLC_NEW_VAL5[] = "apple,seed,crisp,-1,2,0"; static constexpr char AZSLC_ARG6[] = "--azsl6"; static constexpr char AZSLC_ARG7[] = "--azsl7"; //! Helper function. //! Given an input list of {Key, Value} pairs returns a list of strings where each string is of the form: "Key=Value". AZStd::vector CreateListOfStringsFromListOfKeyValues(AZStd::span listOfKeyValues) const { AZStd::vector listOfStrings; for (const auto& keyValue : listOfKeyValues) { if (keyValue.m_value.empty()) { listOfStrings.push_back(keyValue.m_key); } else { listOfStrings.push_back(AZStd::string::format("%s=%s", keyValue.m_key.data(), keyValue.m_value.data())); } } return listOfStrings; } //! Helper function. //! Given an input list of {Key, Value} pairs returns a list of strings where each string is of the form: "Key1", "Value1", "Key2", "Value2". AZStd::vector CreateListOfSingleStringsFromListOfKeyValues(AZStd::span listOfKeyValues) const { AZStd::vector listOfStrings; for (const auto& keyValue : listOfKeyValues) { listOfStrings.push_back(keyValue.m_key); if (!keyValue.m_value.empty()) { listOfStrings.push_back(keyValue.m_value); } } return listOfStrings; } //! Helper function. //! @param outputString: [out] The string " @argName" gets appended to it (The space is intentional). //! Alternatively, if @argValue is NOT empty, then the string " @argName=@argValue" is //! appended to it. //! @param argName: A typical command line argument. "-p" or "--some". //! @param argValue: A string representing the value that should be appended to @argName. void AppendCmdLineArgument(AZStd::string& outputString, AZStd::string_view argName, AZStd::string_view argValue) const { if (argValue.empty()) { outputString += AZStd::string::format(" %s", argName.data()); } else { outputString += AZStd::string::format(" %s=%s", argName.data(), argValue.data()); } } //! Helper function. //! Similar to above, but assumes that @argName refers to just the name of a macro definition so the appended string will always start //! with "-D". void AppendMacroDefinitionArgument(AZStd::string& outputString, AZStd::string_view argName, AZStd::string_view argValue) const { AppendCmdLineArgument(outputString, AZStd::string::format("-D%s", argName.data()), argValue); } //! A helper made of helpers. //! Returns a command line string that results of concatenating the input list of {Key, Value} pairs (with '='). //! Example of a returned string: //! "key1=value1 key2 key3 key4=value" AZStd::string CreateCmdLineStringFromListOfKeyValues(AZStd::span listOfKeyValues) const { AZStd::string cmdLineString; for (const auto& keyValueView : listOfKeyValues) { AppendCmdLineArgument(cmdLineString, keyValueView.m_key, keyValueView.m_value); } return cmdLineString; } //! A helper made of helpers. //! Returns a command line string of macro definitions that results of concatenating the input list of {Key, Value} pairs. //! Example of a returned string: //! "-Dkey1=value1 -Dkey2 -Dkey3 -Dkey4=value" AZStd::string CreateMacroDefinitionCmdLineStringFromListOfKeyValues(AZStd::span listOfKeyValues) const { AZStd::string cmdLineString; for (const auto& keyValueView : listOfKeyValues) { AppendMacroDefinitionArgument(cmdLineString, keyValueView.m_key, keyValueView.m_value); } return cmdLineString; } //! @param includePaths A List of folder paths //! @param predefinedMacros A List of strings with format: "name[=value]" ShaderBuilder::PreprocessorOptions CreatePreprocessorOptions( AZStd::span includePaths, AZStd::span predefinedMacros) const { ShaderBuilder::PreprocessorOptions preprocessorOptions; preprocessorOptions.m_projectIncludePaths.reserve(includePaths.size()); for (const auto& path : includePaths) { preprocessorOptions.m_projectIncludePaths.push_back(path); } preprocessorOptions.m_predefinedMacros.reserve(predefinedMacros.size()); for (const auto& macro : predefinedMacros) { preprocessorOptions.m_predefinedMacros.push_back(macro); } return preprocessorOptions; } //! @param azslcAdditionalFreeArguments: A string representing series of command line arguments for AZSLc. //! @param dxcAdditionalFreeArguments: A string representing series of command line arguments for DXC. RHI::ShaderCompilerArguments CreateShaderCompilerArguments( AZStd::string_view azslcAdditionalFreeArguments, AZStd::string_view dxcAdditionalFreeArguments) const { RHI::ShaderCompilerArguments shaderCompilerArguments; shaderCompilerArguments.m_azslcWarningLevel = 1; shaderCompilerArguments.m_azslcAdditionalFreeArguments = azslcAdditionalFreeArguments; shaderCompilerArguments.m_defaultMatrixOrder = RHI::MatrixOrder::Row; shaderCompilerArguments.m_dxcAdditionalFreeArguments = dxcAdditionalFreeArguments; return shaderCompilerArguments; } //! @param includePaths A List of folder paths //! @param predefinedMacros A List of strings with format: "name[=value]" //! @param azslcAdditionalFreeArguments A string representing series of command line arguments for AZSLc. //! @param dxcAdditionalFreeArguments: A string representing series of command line arguments for DXC. ShaderBuilder::GlobalBuildOptions CreateGlobalBuildOptions( AZStd::span includePaths, AZStd::span predefinedMacros, AZStd::string_view azslcAdditionalFreeArguments, AZStd::string_view dxcAdditionalFreeArguments) const { ShaderBuilder::GlobalBuildOptions globalBuildOptions; globalBuildOptions.m_preprocessorSettings = CreatePreprocessorOptions(includePaths, predefinedMacros); globalBuildOptions.m_compilerArguments = CreateShaderCompilerArguments(azslcAdditionalFreeArguments, dxcAdditionalFreeArguments); return globalBuildOptions; } //! @param name Name of the supervariant. //! @param plusArguments A string with command line arguments that contains both C-preprocessor macro definitions //! and other command line arguments for AZSLc. //! @param minusArguments A string with command line arguments that should be removed from the finalized command line arguments. //! it can contain both, C-preprocessor macro definitions and other command line arguments for AZSLc. RPI::ShaderSourceData::SupervariantInfo CreateSupervariantInfo( AZStd::string_view name, AZStd::string_view plusArguments, AZStd::string_view minusArguments) const { RPI::ShaderSourceData::SupervariantInfo supervariantInfo; supervariantInfo.m_name = name; supervariantInfo.m_plusArguments = plusArguments; supervariantInfo.m_minusArguments = minusArguments; return supervariantInfo; } bool StringContainsAllSubstrings(AZStd::string_view haystack, AZStd::span substrings) { return AZStd::all_of(AZ_BEGIN_END(substrings), [&](AZStd::string_view needle) -> bool { return (haystack.find(needle) != AZStd::string::npos); } ); } bool StringDoesNotContainAnyOneOfTheSubstrings(AZStd::string_view haystack, AZStd::span substrings) { return AZStd::all_of(AZ_BEGIN_END(substrings), [&](AZStd::string_view needle) -> bool { return (haystack.find(needle) == AZStd::string::npos); }); } //! @returns: True if all strings in @substring appear in @vectorOfString. //! @remark: Keep in mind that this is not the same as saying that all strings in @vectorOfStrings appear in @substrings. bool VectorContainsAllSubstrings( AZStd::span vectorOfStrings, AZStd::span substrings) { return AZStd::all_of( AZ_BEGIN_END(substrings), [&](AZStd::string_view needle) -> bool { bool res = AZStd::any_of(AZ_BEGIN_END(vectorOfStrings), [&](AZStd::string_view haystack) -> bool { return haystack.find(needle) != AZStd::string::npos; } ); return res; } ); } //! @returns: True only if None of the strings in @vectorOfStrings contains any of the strings in @substrings. bool VectorDoesNotContainAnyOneOfTheSubstrings(AZStd::span vectorOfStrings, AZStd::span substrings) { return AZStd::all_of(AZ_BEGIN_END(vectorOfStrings), [&](AZStd::string_view haystack) -> bool { return StringDoesNotContainAnyOneOfTheSubstrings(haystack, substrings); }); } }; // class SupervariantCmdArgumentTests TEST_F(SupervariantCmdArgumentTests, CommandLineArgumentUtils_ValidateHelperFunctions) { // In this test the idea is to validate the static helper functions in AZ::RHI::ShaderCompilerArguments class // that are useful for command line argument manipulation, etc. AZStd::vector argumentList = { {AZSLC_ARG1, ""}, {AZSLC_ARG2, AZSLC_VAL2}, {AZSLC_ARG3, AZSLC_VAL3}, {AZSLC_ARG4, ""}, {AZSLC_ARG5, AZSLC_VAL5}, }; auto argumentsAsString = CreateCmdLineStringFromListOfKeyValues(argumentList); auto listOfArgumentNames = AZ::RHI::CommandLineArgumentUtils::GetListOfArgumentNames(argumentsAsString); EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(argumentList), [&](const KeyValueView& needle) -> bool { return (AZStd::find(AZ_BEGIN_END(listOfArgumentNames), needle.m_key) != listOfArgumentNames.end()) && // Make sure the values did not make into the expected list of keys. (AZStd::find(AZ_BEGIN_END(listOfArgumentNames), needle.m_value) == listOfArgumentNames.end()); })); AZStd::vector listOfArgumentsToRemove = { AZSLC_ARG4, AZSLC_ARG2 }; auto stringWithRemovedArguments = AZ::RHI::CommandLineArgumentUtils::RemoveArgumentsFromCommandLineString(listOfArgumentsToRemove, argumentsAsString); EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(listOfArgumentsToRemove), [&](const AZStd::string& needle) -> bool { return stringWithRemovedArguments.find(needle) == AZStd::string::npos; })); AZStd::vector listOfSurvivingArguments = {AZSLC_ARG1, AZSLC_ARG3, AZSLC_ARG5}; EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(listOfSurvivingArguments), [&](const AZStd::string& needle) -> bool { return stringWithRemovedArguments.find(needle) != AZStd::string::npos; })); auto stringWithoutExtraSpaces = AZ::RHI::CommandLineArgumentUtils::RemoveExtraSpaces(" --arg1 -arg2 --arg3=foo --arg4=bar "); EXPECT_EQ(stringWithoutExtraSpaces, AZStd::string("--arg1 -arg2 --arg3=foo --arg4=bar")); auto stringAsMergedArguments = AZ::RHI::CommandLineArgumentUtils::MergeCommandLineArguments("--arg1 -arg2 --arg3=foo", "--arg3=bar --arg4"); EXPECT_EQ(stringAsMergedArguments, AZStd::string("--arg1 -arg2 --arg3=bar --arg4")); EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("-DMACRO")); EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("-D MACRO")); EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -D MACRO")); EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p -DMACRO --more")); EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p -D MACRO=VALUE --more")); EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p --more")); EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p --more --DFAKE")); EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--DFAKE1 --help -p --more --D FAKE2")); } TEST_F(SupervariantCmdArgumentTests, ShaderCompilerArguments_ValidateCommandLineArgumentsMerge) { // In this test we validate that AZ::RHI::ShaderCompilerArguments::Merge() works as expected // by merging AZSLC & DXC arguments giving higher priority to the arguments in the "right". auto shaderCompilerArgumentsLeft = CreateShaderCompilerArguments( "--azsl1 --azsl2=avalue2a -azsl3 --azsl4=avalue4a", "--dxc1=dvalue1a -dxc2 --dxc3=dvalue3a --dxc4"); auto shaderCompilerArgumentsRight = CreateShaderCompilerArguments( "--azsl1 --azsl2=avalue2b -azsl3 --azsl4=avalue4a --azsl5", "--dxc1=dvalue1a -dxc2 --dxc3=dvalue3b --dxc4 --dxc5=dvalue5a"); shaderCompilerArgumentsLeft.Merge(shaderCompilerArgumentsRight); EXPECT_EQ(shaderCompilerArgumentsLeft.m_azslcAdditionalFreeArguments, "--azsl1 --azsl2=avalue2b -azsl3 --azsl4=avalue4a --azsl5"); EXPECT_EQ(shaderCompilerArgumentsLeft.m_dxcAdditionalFreeArguments, "--dxc1=dvalue1a -dxc2 --dxc3=dvalue3b --dxc4 --dxc5=dvalue5a"); } TEST_F(SupervariantCmdArgumentTests, SupervariantInfo_ValidateMemberFunctions) { // In this test all member functions of the ShaderSourceData::SupervariantInfo class // are validated. AZStd::vector mcppMacrosList = { {MCPP_MACRO1, MCPP_VALUE1}, {MCPP_MACRO2, MCPP_VALUE2}, {MCPP_MACRO3, MCPP_VALUE3}, {MCPP_MACRO4, ""}, }; AZStd::string argumentsToAddOrReplace; AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO3, MCPP_NEW_VALUE3); AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG2, AZSLC_NEW_VAL2a); AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO1, MCPP_NEW_VALUE1); AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG5, AZSLC_NEW_VAL5); AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO5, ""); AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG6, ""); AZStd::string argumentsToRemove; AppendCmdLineArgument(argumentsToRemove, AZSLC_ARG3, ""); AppendMacroDefinitionArgument(argumentsToRemove, MCPP_MACRO2, ""); AppendCmdLineArgument(argumentsToRemove, AZSLC_ARG4, ""); AppendMacroDefinitionArgument(argumentsToRemove, MCPP_MACRO4, ""); auto supervariantInfo = CreateSupervariantInfo("Dummy", argumentsToAddOrReplace, argumentsToRemove); auto macroListToRemove = supervariantInfo.GetCombinedListOfMacroDefinitionNamesToRemove(); AZStd::vector macroNamesToRemoveThatMustBePresent = { MCPP_MACRO1, MCPP_MACRO2, MCPP_MACRO3, MCPP_MACRO4, MCPP_MACRO5 }; EXPECT_EQ(macroListToRemove.size(), macroNamesToRemoveThatMustBePresent.size()); EXPECT_TRUE( VectorContainsAllSubstrings(macroListToRemove, macroNamesToRemoveThatMustBePresent) ); auto macroListToAdd = supervariantInfo.GetMacroDefinitionsToAdd(); AZStd::vector macroNamesToAddThatMustBePresent = {MCPP_MACRO1, MCPP_MACRO3, MCPP_MACRO5}; EXPECT_EQ(macroListToAdd.size(), macroNamesToAddThatMustBePresent.size()); EXPECT_TRUE(VectorContainsAllSubstrings(macroListToAdd, macroNamesToAddThatMustBePresent)); // The result of GetCustomizedArgumentsForAzslc() is the most important value to test AZStd::vector freeAzslcArgumentList = { {AZSLC_ARG1, ""}, {AZSLC_ARG2, AZSLC_VAL2}, {AZSLC_ARG3, AZSLC_VAL3}, {AZSLC_ARG4, ""}, {AZSLC_ARG5, AZSLC_VAL5}, }; AZStd::string azslcArgs = CreateCmdLineStringFromListOfKeyValues(freeAzslcArgumentList); AZStd::string customizedAzslcArgs = supervariantInfo.GetCustomizedArgumentsForAzslc(azslcArgs); AZStd::vector stringsThatMustBePresent = { AZSLC_ARG1, AZSLC_ARG2, AZSLC_NEW_VAL2a, AZSLC_ARG5, AZSLC_NEW_VAL5, AZSLC_ARG6}; EXPECT_TRUE(StringContainsAllSubstrings(customizedAzslcArgs, stringsThatMustBePresent)); AZStd::vector stringsThatCanNotBePresent = { AZSLC_ARG3, AZSLC_VAL3, AZSLC_ARG4, // Because GetCustomizedArgumentsForAzslc() only returns arguments for AZSLc, none of the macro related // arguments can be present MCPP_MACRO1, MCPP_VALUE1, MCPP_NEW_VALUE1, MCPP_MACRO2, MCPP_VALUE2, MCPP_MACRO3, MCPP_VALUE3, MCPP_NEW_VALUE3, MCPP_MACRO4, MCPP_MACRO5 }; EXPECT_TRUE( StringDoesNotContainAnyOneOfTheSubstrings(customizedAzslcArgs, stringsThatCanNotBePresent) ); } TEST_F(SupervariantCmdArgumentTests, ShaderAssetBuilder_ValidateInfluenceOfSupervariantInfoOnGlobalBuildOptions) { // In this test we validate how the ShaderAssetBuilder configure the commmand line arguments it passes // to MCPP, AZSLc & DXC. It basically starts with a GlobalBuildOptions, that gets further customized by // the ShaderCompilerArguments from ShaderSourceData(.shader file) and later further customized // by each SupervariantInfo in ShaderSourceData. // The first step is to define the initial values of the GlobalBuildOptions. AZStd::vector globalMcppMacrosList = { {MCPP_MACRO1, MCPP_VALUE1}, {MCPP_MACRO2, MCPP_VALUE2}, {MCPP_MACRO3, MCPP_VALUE3}, {MCPP_MACRO4, ""}, }; AZStd::vector globalAzslArguments = { {AZSLC_ARG1, ""}, {AZSLC_ARG2, AZSLC_VAL2}, {AZSLC_ARG3, AZSLC_VAL3}, {AZSLC_ARG4, ""}, {AZSLC_ARG5, AZSLC_VAL5}, }; auto globalBuildOptions = CreateGlobalBuildOptions( AZStd::vector(), CreateListOfStringsFromListOfKeyValues(globalMcppMacrosList), CreateCmdLineStringFromListOfKeyValues(globalAzslArguments), "" /* Don't care about DXC in this test */); // The second step is to load the Shader Compiler Arguments from the .shader file. // These arguments will be merged in @globalBuildOptions, but the .shader arguments have // higher priority. AZStd::vector shaderAzslArguments = { {AZSLC_ARG2, AZSLC_NEW_VAL2a}, {AZSLC_ARG6, ""}, }; auto shaderCompilerArguments = CreateShaderCompilerArguments( CreateCmdLineStringFromListOfKeyValues(shaderAzslArguments), "" /* Don't care about DXC in this test */); globalBuildOptions.m_compilerArguments.Merge(shaderCompilerArguments); // Let's create the dummy supervariant. It will have some MCPP & AZSLc arguments to be added/replaced AND other MCPP & AZSLc arguments to be removed. AZStd::vector supervariantAzslArgumentsToAdd = { {AZSLC_ARG2, AZSLC_NEW_VAL2b}, {AZSLC_ARG7, ""}, }; AZStd::vector supervariantMacroDefinitionsToAdd = { {MCPP_MACRO1, MCPP_NEW_VALUE1}, {MCPP_MACRO3, MCPP_NEW_VALUE3}, {MCPP_MACRO5, ""}, }; auto supervariantArgumentsToAdd = CreateCmdLineStringFromListOfKeyValues(supervariantAzslArgumentsToAdd) + CreateMacroDefinitionCmdLineStringFromListOfKeyValues(supervariantMacroDefinitionsToAdd); AZStd::vector supervariantAzslArgumentsToRemove = { {AZSLC_ARG4, ""}, {AZSLC_ARG1, ""}, }; AZStd::vector supervariantMacrosToRemove = { {MCPP_MACRO2, ""}, {MCPP_MACRO4, ""}, }; auto supervariantArgumentsToRemove = CreateCmdLineStringFromListOfKeyValues(supervariantAzslArgumentsToRemove) + CreateMacroDefinitionCmdLineStringFromListOfKeyValues(supervariantMacrosToRemove); //CreateMacroDefinitionCmdLineStringFromListOfKeyValues auto supervariantInfo = CreateSupervariantInfo("Dummy", supervariantArgumentsToAdd, // These arguments will be added or replace existing ones. supervariantArgumentsToRemove); // These arguments must be removed. AZStd::vector macroDefinitionNamesToRemove = supervariantInfo.GetCombinedListOfMacroDefinitionNamesToRemove(); globalBuildOptions.m_preprocessorSettings.RemovePredefinedMacros(macroDefinitionNamesToRemove); AZStd::vector macroDefinitionsToAdd = supervariantInfo.GetMacroDefinitionsToAdd(); globalBuildOptions.m_preprocessorSettings.m_predefinedMacros.insert( globalBuildOptions.m_preprocessorSettings.m_predefinedMacros.end(), macroDefinitionsToAdd.begin(), macroDefinitionsToAdd.end()); // Validate macro definitions that must be present. EXPECT_TRUE( VectorContainsAllSubstrings( globalBuildOptions.m_preprocessorSettings.m_predefinedMacros, AZStd::vector({MCPP_MACRO1, MCPP_NEW_VALUE1, MCPP_MACRO3, MCPP_NEW_VALUE3, MCPP_MACRO5})) ); // Validate macro definitions that can't be present. EXPECT_TRUE( VectorDoesNotContainAnyOneOfTheSubstrings( globalBuildOptions.m_preprocessorSettings.m_predefinedMacros, AZStd::vector({MCPP_MACRO2, MCPP_VALUE3, MCPP_MACRO4})) ); AZStd::string azslcArgsFromGlobalBuildOptions = globalBuildOptions.m_compilerArguments.MakeAdditionalAzslcCommandLineString(); // The result of GetCustomizedArgumentsForAzslc() is the most important value to test AZStd::string customizedAzslcArgs = supervariantInfo.GetCustomizedArgumentsForAzslc(azslcArgsFromGlobalBuildOptions); EXPECT_TRUE( StringContainsAllSubstrings(customizedAzslcArgs, CreateListOfSingleStringsFromListOfKeyValues(supervariantAzslArgumentsToAdd)) ); EXPECT_TRUE( StringDoesNotContainAnyOneOfTheSubstrings(customizedAzslcArgs, CreateListOfSingleStringsFromListOfKeyValues(supervariantAzslArgumentsToRemove)) ); EXPECT_TRUE( StringContainsAllSubstrings(customizedAzslcArgs, AZStd::vector({AZSLC_ARG3, AZSLC_VAL3, AZSLC_ARG5, AZSLC_VAL5})) ); } } //namespace UnitTest AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);