Skip to content

Commit b3fa88e

Browse files
authored
Process actions sync. on startup; don't dupe nonexistent profile (#5090)
This PR has evolved to encapsulate two related fixes that I can't really untie anymore. #2455 - Duplicating a tab that doesn't exist anymore This was the bug I was originally fixing in #4429. When the user tries to `duplicateTab` with a profile that doesn't exist anymore (like might happen after a settings reload), don't crash. As I was going about adding tests for this, got blocked by the fact that the Terminal couldn't open _any_ panes while the `TerminalPage` was size 0x0. This had two theoretical solutions: * Fake the `TerminalPage` into thinking it had a real size in the test - probably possible, though I'm unsure how it would work in practice. * Change `Pane`s to not require an `ActualWidth`, `ActualHeight` on initialization. Fortuately, the second option was something else that was already on my backlog of bugs. #4618 - `wt` command-line can't consistently parse more than one arg Presently, the Terminal just arbitrarily dispatches a bunch of handlers to try and handle all the commands provided on the commandline. That's lead to a bunch of reports that not all the commands will always get executed, nor will they all get executed in the same order. This PR also changes the `TerminalPage` to be able to dispatch all the commands sequentially, all at once in the startup. No longer will there be a hot second where the commands seem to execute themselves in from of the user - they'll all happen behind the scenes on startup. This involved a couple other changes areound the `TerminalPage` * I had to make sure that panes could be opened at a 0x0 size. Now they use a star sizing based off the percentage of the parent they're supposed to consume, so that when the parent _does_ get laid out, they'll take the appropriate size of that parent. * I had to do some math ahead of time to try and calculate what a `SplitState::Automatic` would be evaluated as, despite the fact that we don't actually know how big the pane will be. * I had to ensure that `focus-tab` commands appropriately mark a single tab as focused while we're in startup, without roundtripping to the Dispatcher thread and back ## References #4429 - the original PR for #2455 #5047 - a follow-up task from discussion in #4429 #4953 - a PR for making panes use star sizing, which was immensly helpful for this PR. ## Detailed Description of the Pull Request / Additional comments `CascadiaSettings::BuildSettings` can throw if the GUID doesn't exist. This wraps those calls up with a try/catch. It also adds a couple tests - a few `SettingsTests` for try/catching this state. It also adds a XAML-y test in `TabTests` that creates a `TerminalPage` and then performs som UI-like actions on it. This test required a minor change to how we generate the new tab dropdown - in the tests, `Application::Current()` is _not_ a `TerminalApp::App`, so it doesn't have a `Logic()` to query. So wrap that in a try/catch as well. While working on these tests, I found that we'd crash pretty agressively for mysterious reasons if the TestHostApp became focused while the test was running. This was due to a call in `TSFInputControl::NotifyFocusEnter` that would callback to `TSFInputControl::_layoutRequested`, which would crash on setting the `MaxSize` of the canvas to a negative value. This PR includes a hotfix for that bug as well. ## Validation Steps Performed * Manual testing with a _lot_ of commands in a commandline * run the tests * Team tested in selfhost Closes #2455 Closes #4618
1 parent 0461a2a commit b3fa88e

File tree

11 files changed

+879
-148
lines changed

11 files changed

+879
-148
lines changed

src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ namespace TerminalAppLocalTests
7070

7171
TEST_METHOD(TestTerminalArgsForBinding);
7272

73+
TEST_METHOD(FindMissingProfile);
74+
TEST_METHOD(MakeSettingsForProfileThatDoesntExist);
75+
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
76+
7377
TEST_METHOD(TestLayerProfileOnColorScheme);
7478

7579
TEST_METHOD(ValidateKeybindingsWarnings);
@@ -2094,6 +2098,146 @@ namespace TerminalAppLocalTests
20942098
}
20952099
}
20962100

2101+
void SettingsTests::FindMissingProfile()
2102+
{
2103+
// Test that CascadiaSettings::FindProfile returns null for a GUID that
2104+
// doesn't exist
2105+
const std::string settingsString{ R"(
2106+
{
2107+
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
2108+
"profiles": [
2109+
{
2110+
"name" : "profile0",
2111+
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
2112+
},
2113+
{
2114+
"name" : "profile1",
2115+
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
2116+
}
2117+
]
2118+
})" };
2119+
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
2120+
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
2121+
2122+
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
2123+
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
2124+
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
2125+
2126+
const Profile* const profile1 = settings->FindProfile(guid1);
2127+
const Profile* const profile2 = settings->FindProfile(guid2);
2128+
const Profile* const profile3 = settings->FindProfile(guid3);
2129+
2130+
VERIFY_IS_NOT_NULL(profile1);
2131+
VERIFY_IS_NOT_NULL(profile2);
2132+
VERIFY_IS_NULL(profile3);
2133+
2134+
VERIFY_ARE_EQUAL(L"profile0", profile1->GetName());
2135+
VERIFY_ARE_EQUAL(L"profile1", profile2->GetName());
2136+
}
2137+
2138+
void SettingsTests::MakeSettingsForProfileThatDoesntExist()
2139+
{
2140+
// Test that MakeSettings throws when the GUID doesn't exist
2141+
const std::string settingsString{ R"(
2142+
{
2143+
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
2144+
"profiles": [
2145+
{
2146+
"name" : "profile0",
2147+
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
2148+
"historySize": 1
2149+
},
2150+
{
2151+
"name" : "profile1",
2152+
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
2153+
"historySize": 2
2154+
}
2155+
]
2156+
})" };
2157+
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
2158+
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
2159+
2160+
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
2161+
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
2162+
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
2163+
2164+
try
2165+
{
2166+
auto terminalSettings = settings->BuildSettings(guid1);
2167+
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
2168+
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
2169+
}
2170+
catch (...)
2171+
{
2172+
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
2173+
}
2174+
2175+
try
2176+
{
2177+
auto terminalSettings = settings->BuildSettings(guid2);
2178+
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
2179+
VERIFY_ARE_EQUAL(2, terminalSettings.HistorySize());
2180+
}
2181+
catch (...)
2182+
{
2183+
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
2184+
}
2185+
2186+
VERIFY_THROWS(auto terminalSettings = settings->BuildSettings(guid3), wil::ResultException, L"This call to BuildSettings should fail");
2187+
2188+
try
2189+
{
2190+
const auto [guid, termSettings] = settings->BuildSettings(nullptr);
2191+
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
2192+
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
2193+
}
2194+
catch (...)
2195+
{
2196+
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
2197+
}
2198+
}
2199+
2200+
void SettingsTests::MakeSettingsForDefaultProfileThatDoesntExist()
2201+
{
2202+
// Test that MakeSettings _doesnt_ throw when we load settings with a
2203+
// defaultProfile that's not in the list, we validate the settings, and
2204+
// then call MakeSettings(nullopt). The validation should ensure that
2205+
// the default profile is something reasonable
2206+
const std::string settingsString{ R"(
2207+
{
2208+
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
2209+
"profiles": [
2210+
{
2211+
"name" : "profile0",
2212+
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
2213+
"historySize": 1
2214+
},
2215+
{
2216+
"name" : "profile1",
2217+
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
2218+
"historySize": 2
2219+
}
2220+
]
2221+
})" };
2222+
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
2223+
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
2224+
settings->_ValidateSettings();
2225+
2226+
VERIFY_ARE_EQUAL(2u, settings->_warnings.size());
2227+
VERIFY_ARE_EQUAL(2u, settings->_profiles.size());
2228+
VERIFY_ARE_EQUAL(settings->_globals.GetDefaultProfile(), settings->_profiles.at(0).GetGuid());
2229+
try
2230+
{
2231+
const auto [guid, termSettings] = settings->BuildSettings(nullptr);
2232+
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
2233+
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
2234+
}
2235+
catch (...)
2236+
{
2237+
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
2238+
}
2239+
}
2240+
20972241
void SettingsTests::TestLayerProfileOnColorScheme()
20982242
{
20992243
Log::Comment(NoThrowString().Format(

0 commit comments

Comments
 (0)