// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. #include "pch.h" #include "../TerminalSettingsModel/ColorScheme.h" #include "../TerminalSettingsModel/CascadiaSettings.h" #include "JsonTestClass.h" using namespace Microsoft::Console; using namespace winrt::Microsoft::Terminal::Settings::Model::implementation; using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace WEX::Common; namespace SettingsModelLocalTests { // TODO:microsoft/terminal#3838: // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for // an updated TAEF that will let us install framework packages when the test // package is deployed. Until then, these tests won't deploy in CI. class ColorSchemeTests : public JsonTestClass { // Use a custom AppxManifest to ensure that we can activate winrt types // from our test. This property will tell taef to manually use this as // the AppxManifest for this test class. // This does not yet work for anything XAML-y. See TabTests.cpp for more // details on that. BEGIN_TEST_CLASS(ColorSchemeTests) TEST_CLASS_PROPERTY(L"RunAs", L"UAP") TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") END_TEST_CLASS() TEST_METHOD(CanLayerColorScheme); TEST_METHOD(LayerColorSchemeProperties); TEST_METHOD(LayerColorSchemesOnArray); TEST_METHOD(UpdateSchemeReferences); TEST_CLASS_SETUP(ClassSetup) { InitializeJsonReader(); return true; } }; void ColorSchemeTests::CanLayerColorScheme() { const std::string scheme0String{ R"({ "name": "scheme0", "foreground": "#000000", "background": "#010101" })" }; const std::string scheme1String{ R"({ "name": "scheme1", "foreground": "#020202", "background": "#030303" })" }; const std::string scheme2String{ R"({ "name": "scheme0", "foreground": "#040404", "background": "#050505" })" }; const std::string scheme3String{ R"({ // "name": "scheme3", "foreground": "#060606", "background": "#070707" })" }; const auto scheme0Json = VerifyParseSucceeded(scheme0String); const auto scheme1Json = VerifyParseSucceeded(scheme1String); const auto scheme2Json = VerifyParseSucceeded(scheme2String); const auto scheme3Json = VerifyParseSucceeded(scheme3String); const auto scheme0 = ColorScheme::FromJson(scheme0Json); VERIFY_IS_TRUE(scheme0->ShouldBeLayered(scheme0Json)); VERIFY_IS_FALSE(scheme0->ShouldBeLayered(scheme1Json)); VERIFY_IS_TRUE(scheme0->ShouldBeLayered(scheme2Json)); VERIFY_IS_FALSE(scheme0->ShouldBeLayered(scheme3Json)); const auto scheme1 = ColorScheme::FromJson(scheme1Json); VERIFY_IS_FALSE(scheme1->ShouldBeLayered(scheme0Json)); VERIFY_IS_TRUE(scheme1->ShouldBeLayered(scheme1Json)); VERIFY_IS_FALSE(scheme1->ShouldBeLayered(scheme2Json)); VERIFY_IS_FALSE(scheme1->ShouldBeLayered(scheme3Json)); const auto scheme3 = ColorScheme::FromJson(scheme3Json); VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme0Json)); VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme1Json)); VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme2Json)); VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme3Json)); } void ColorSchemeTests::LayerColorSchemeProperties() { const std::string scheme0String{ R"({ "name": "scheme0", "foreground": "#000000", "background": "#010101", "selectionBackground": "#010100", "cursorColor": "#010001", "red": "#010000", "green": "#000100", "blue": "#000001" })" }; const std::string scheme1String{ R"({ "name": "scheme1", "foreground": "#020202", "background": "#030303", "selectionBackground": "#020200", "cursorColor": "#040004", "red": "#020000", "blue": "#000002" })" }; const std::string scheme2String{ R"({ "name": "scheme0", "foreground": "#040404", "background": "#050505", "selectionBackground": "#030300", "cursorColor": "#060006", "red": "#030000", "green": "#000300" })" }; const auto scheme0Json = VerifyParseSucceeded(scheme0String); const auto scheme1Json = VerifyParseSucceeded(scheme1String); const auto scheme2Json = VerifyParseSucceeded(scheme2String); auto scheme0 = ColorScheme::FromJson(scheme0Json); VERIFY_ARE_EQUAL(L"scheme0", scheme0->_Name); VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0->_Foreground); VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background); VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 0), scheme0->_SelectionBackground); VERIFY_ARE_EQUAL(ARGB(0, 1, 0, 1), scheme0->_CursorColor); VERIFY_ARE_EQUAL(ARGB(0, 1, 0, 0), scheme0->_table[XTERM_RED_ATTR]); VERIFY_ARE_EQUAL(ARGB(0, 0, 1, 0), scheme0->_table[XTERM_GREEN_ATTR]); VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 1), scheme0->_table[XTERM_BLUE_ATTR]); Log::Comment(NoThrowString().Format( L"Layering scheme1 on top of scheme0")); scheme0->LayerJson(scheme1Json); VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme0->_Foreground); VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme0->_Background); VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 0), scheme0->_SelectionBackground); VERIFY_ARE_EQUAL(ARGB(0, 4, 0, 4), scheme0->_CursorColor); VERIFY_ARE_EQUAL(ARGB(0, 2, 0, 0), scheme0->_table[XTERM_RED_ATTR]); VERIFY_ARE_EQUAL(ARGB(0, 0, 1, 0), scheme0->_table[XTERM_GREEN_ATTR]); VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 2), scheme0->_table[XTERM_BLUE_ATTR]); Log::Comment(NoThrowString().Format( L"Layering scheme2Json on top of (scheme0+scheme1)")); scheme0->LayerJson(scheme2Json); VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0->_Foreground); VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background); VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 0), scheme0->_SelectionBackground); VERIFY_ARE_EQUAL(ARGB(0, 6, 0, 6), scheme0->_CursorColor); VERIFY_ARE_EQUAL(ARGB(0, 3, 0, 0), scheme0->_table[XTERM_RED_ATTR]); VERIFY_ARE_EQUAL(ARGB(0, 0, 3, 0), scheme0->_table[XTERM_GREEN_ATTR]); VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 2), scheme0->_table[XTERM_BLUE_ATTR]); } void ColorSchemeTests::LayerColorSchemesOnArray() { const std::string scheme0String{ R"({ "name": "scheme0", "foreground": "#000000", "background": "#010101" })" }; const std::string scheme1String{ R"({ "name": "scheme1", "foreground": "#020202", "background": "#030303" })" }; const std::string scheme2String{ R"({ "name": "scheme0", "foreground": "#040404", "background": "#050505" })" }; const std::string scheme3String{ R"({ // by not providing a name, the scheme will have the name "" "foreground": "#060606", "background": "#070707" })" }; const auto scheme0Json = VerifyParseSucceeded(scheme0String); const auto scheme1Json = VerifyParseSucceeded(scheme1String); const auto scheme2Json = VerifyParseSucceeded(scheme2String); const auto scheme3Json = VerifyParseSucceeded(scheme3String); auto settings = winrt::make_self(); VERIFY_ARE_EQUAL(0u, settings->_globals->ColorSchemes().Size()); VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme0Json)); VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme1Json)); VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme2Json)); VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json)); settings->_LayerOrCreateColorScheme(scheme0Json); { for (auto kv : settings->_globals->ColorSchemes()) { Log::Comment(NoThrowString().Format( L"kv:%s->%s", kv.Key().data(), kv.Value().Name().data())); } VERIFY_ARE_EQUAL(1u, settings->_globals->ColorSchemes().Size()); VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0")); auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0"); auto scheme0 = winrt::get_self(scheme0Proj); VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json)); VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme1Json)); VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json)); VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json)); VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0->_Foreground); VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background); } settings->_LayerOrCreateColorScheme(scheme1Json); { VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size()); VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0")); auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0"); auto scheme0 = winrt::get_self(scheme0Proj); VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1")); auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1"); auto scheme1 = winrt::get_self(scheme1Proj); VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json)); VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme1Json)); VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json)); VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json)); VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0->_Foreground); VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background); VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground); VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background); } settings->_LayerOrCreateColorScheme(scheme2Json); { VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size()); VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0")); auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0"); auto scheme0 = winrt::get_self(scheme0Proj); VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1")); auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1"); auto scheme1 = winrt::get_self(scheme1Proj); VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json)); VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme1Json)); VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json)); VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json)); VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0->_Foreground); VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background); VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground); VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background); } settings->_LayerOrCreateColorScheme(scheme3Json); { VERIFY_ARE_EQUAL(3u, settings->_globals->ColorSchemes().Size()); VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0")); auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0"); auto scheme0 = winrt::get_self(scheme0Proj); VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1")); auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1"); auto scheme1 = winrt::get_self(scheme1Proj); VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"")); auto scheme2Proj = settings->_globals->ColorSchemes().Lookup(L""); auto scheme2 = winrt::get_self(scheme2Proj); VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json)); VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme1Json)); VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json)); VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json)); VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0->_Foreground); VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background); VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground); VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background); VERIFY_ARE_EQUAL(ARGB(0, 6, 6, 6), scheme2->_Foreground); VERIFY_ARE_EQUAL(ARGB(0, 7, 7, 7), scheme2->_Background); } } void ColorSchemeTests::UpdateSchemeReferences() { const std::string settingsString{ R"json({ "defaultProfile": "Inherited reference", "profiles": { "defaults": { "colorScheme": "Scheme 1" }, "list": [ { "name": "Explicit scheme reference", "colorScheme": "Scheme 1" }, { "name": "Explicit reference; hidden", "colorScheme": "Scheme 1", "hidden": true }, { "name": "Inherited reference" }, { "name": "Different reference", "colorScheme": "Scheme 2" } ] }, "schemes": [ { "name": "Scheme 1" }, { "name": "Scheme 2" }, { "name": "Scheme 1 (renamed)" } ] })json" }; auto settings{ winrt::make_self(false) }; settings->_ParseJsonString(settingsString, false); settings->_ApplyDefaultsFromUserSettings(); settings->LayerJson(settings->_userSettings); settings->_ValidateSettings(); // update all references to "Scheme 1" const auto newName{ L"Scheme 1 (renamed)" }; settings->UpdateColorSchemeReferences(L"Scheme 1", newName); // verify profile defaults Log::Comment(L"Profile Defaults"); VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().ColorSchemeName()); VERIFY_IS_TRUE(settings->ProfileDefaults().HasColorSchemeName()); // verify all other profiles const auto& profiles{ settings->AllProfiles() }; { const auto& prof{ profiles.GetAt(0) }; Log::Comment(prof.Name().c_str()); VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName()); VERIFY_IS_TRUE(prof.HasColorSchemeName()); } { const auto& prof{ profiles.GetAt(1) }; Log::Comment(prof.Name().c_str()); VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName()); VERIFY_IS_TRUE(prof.HasColorSchemeName()); } { const auto& prof{ profiles.GetAt(2) }; Log::Comment(prof.Name().c_str()); VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName()); VERIFY_IS_FALSE(prof.HasColorSchemeName()); } { const auto& prof{ profiles.GetAt(3) }; Log::Comment(prof.Name().c_str()); VERIFY_ARE_EQUAL(L"Scheme 2", prof.ColorSchemeName()); VERIFY_IS_TRUE(prof.HasColorSchemeName()); } } }