9708a75131
Ensures that command-lines constructed to invoke `wt` are escaped properly. ## PR Checklist * [x] Closes #11273 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed ## Detailed Description of the Pull Request / Additional comments This was broken in two places - when constructing the command-line in the shell extension and in `NewTerminalArgs::ToCommandline()`. Both places now invoke a shared method to escape the command-line arguments that require it. ## Validation Steps Performed Added a test and additionally: * Invoked the shell extension from `D:\Downloads\With;Semicolon`. * Added a `newWindow` action to `settings.json` as below and ensured the new window opened without erroring. ```json { "command": { "action": "newWindow", "tabTitle": "\";foo\\" }, "keys": "ctrl+shift+s" } ```
610 lines
30 KiB
C++
610 lines
30 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
|
|
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
|
#include "JsonTestClass.h"
|
|
#include "TestUtils.h"
|
|
|
|
using namespace Microsoft::Console;
|
|
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
|
using namespace winrt::Microsoft::Terminal::Control;
|
|
using namespace winrt::Windows::Foundation::Collections;
|
|
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 CommandTests : 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(CommandTests)
|
|
TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
|
|
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
|
END_TEST_CLASS()
|
|
|
|
TEST_METHOD(ManyCommandsSameAction);
|
|
TEST_METHOD(LayerCommand);
|
|
TEST_METHOD(TestSplitPaneArgs);
|
|
TEST_METHOD(TestSplitPaneBadSize);
|
|
TEST_METHOD(TestResourceKeyName);
|
|
TEST_METHOD(TestAutogeneratedName);
|
|
TEST_METHOD(TestLayerOnAutogeneratedName);
|
|
|
|
TEST_METHOD(TestGenerateCommandline);
|
|
};
|
|
|
|
void CommandTests::ManyCommandsSameAction()
|
|
{
|
|
const std::string commands0String{ R"([ { "name":"action0", "command": "copy" } ])" };
|
|
const std::string commands1String{ R"([ { "name":"action1", "command": { "action": "copy", "singleLine": false } } ])" };
|
|
const std::string commands2String{ R"([
|
|
{ "name":"action2", "command": "paste" },
|
|
{ "name":"action3", "command": "paste" }
|
|
])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
const auto commands1Json = VerifyParseSucceeded(commands1String);
|
|
const auto commands2Json = VerifyParseSucceeded(commands2String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
}
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands1Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
}
|
|
VERIFY_ARE_EQUAL(2u, commands.Size());
|
|
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands2Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
}
|
|
VERIFY_ARE_EQUAL(4u, commands.Size());
|
|
}
|
|
|
|
void CommandTests::LayerCommand()
|
|
{
|
|
// Each one of the commands in this test should layer upon the previous, overriding the action.
|
|
const std::string commands0String{ R"([ { "name":"action0", "command": "copy" } ])" };
|
|
const std::string commands1String{ R"([ { "name":"action0", "command": "paste" } ])" };
|
|
const std::string commands2String{ R"([ { "name":"action0", "command": "newTab" } ])" };
|
|
const std::string commands3String{ R"([ { "name":"action0", "command": null } ])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
const auto commands1Json = VerifyParseSucceeded(commands1String);
|
|
const auto commands2Json = VerifyParseSucceeded(commands2String);
|
|
const auto commands3Json = VerifyParseSucceeded(commands3String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
auto command = commands.Lookup(L"action0");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<CopyTextArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
}
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands1Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
auto command = commands.Lookup(L"action0");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::PasteText, command.ActionAndArgs().Action());
|
|
VERIFY_IS_NULL(command.ActionAndArgs().Args());
|
|
}
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands2Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
auto command = commands.Lookup(L"action0");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewTabArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
}
|
|
{
|
|
// This last command should "unbind" the action.
|
|
auto warnings = implementation::Command::LayerJson(commands, commands3Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
}
|
|
}
|
|
|
|
void CommandTests::TestSplitPaneArgs()
|
|
{
|
|
// This is the same as KeyBindingsTests::TestSplitPaneArgs, but with
|
|
// looking up the action and its args from a map of commands, instead
|
|
// of from keybindings.
|
|
|
|
const std::string commands0String{ R"([
|
|
{ "name": "command1", "command": { "action": "splitPane", "split": "vertical" } },
|
|
{ "name": "command2", "command": { "action": "splitPane", "split": "horizontal" } },
|
|
{ "name": "command4", "command": { "action": "splitPane" } },
|
|
{ "name": "command5", "command": { "action": "splitPane", "split": "auto" } },
|
|
{ "name": "command6", "command": { "action": "splitPane", "size": 0.25 } },
|
|
{ "name": "command7", "command": { "action": "splitPane", "split": "right" } },
|
|
{ "name": "command8", "command": { "action": "splitPane", "split": "left" } },
|
|
{ "name": "command9", "command": { "action": "splitPane", "split": "up" } },
|
|
{ "name": "command10", "command": { "action": "splitPane", "split": "down" } },
|
|
])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(9u, commands.Size());
|
|
|
|
{
|
|
auto command = commands.Lookup(L"command1");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command2");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command4");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command5");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command6");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
|
|
VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command7");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command8");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Left, realArgs.SplitDirection());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command9");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Up, realArgs.SplitDirection());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command10");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
}
|
|
|
|
void CommandTests::TestSplitPaneBadSize()
|
|
{
|
|
const std::string commands0String{ R"([
|
|
{ "name": "command1", "command": { "action": "splitPane", "size": 0.25 } },
|
|
{ "name": "command2", "command": { "action": "splitPane", "size": 1.0 } },
|
|
{ "name": "command3", "command": { "action": "splitPane", "size": 0 } },
|
|
{ "name": "command4", "command": { "action": "splitPane", "size": 50 } },
|
|
])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(3u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
|
|
{
|
|
auto command = commands.Lookup(L"command1");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
|
|
VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize());
|
|
}
|
|
}
|
|
|
|
void CommandTests::TestResourceKeyName()
|
|
{
|
|
// This test checks looking up a name from a resource key.
|
|
|
|
const std::string commands0String{ R"([ { "name": { "key": "DuplicateTabCommandKey"}, "command": "copy" } ])" };
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
|
|
// NOTE: We're relying on DuplicateTabCommandKey being defined as
|
|
// "Duplicate Tab" here. If that string changes in our resources,
|
|
// this test will break.
|
|
auto command = commands.Lookup(L"Duplicate tab");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<CopyTextArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
}
|
|
}
|
|
|
|
void CommandTests::TestAutogeneratedName()
|
|
{
|
|
// This test ensures that we'll correctly create commands for actions
|
|
// that don't have given names, pursuant to the spec in GH#6532.
|
|
|
|
// NOTE: The keys used to look up these commands are partially generated
|
|
// from strings in our Resources.resw. If those string values should
|
|
// change, it's likely that this test will break.
|
|
|
|
const std::string commands0String{ R"([
|
|
{ "command": { "action": "splitPane", "split": null } },
|
|
{ "command": { "action": "splitPane", "split": "left" } },
|
|
{ "command": { "action": "splitPane", "split": "right" } },
|
|
{ "command": { "action": "splitPane", "split": "up" } },
|
|
{ "command": { "action": "splitPane", "split": "down" } },
|
|
{ "command": { "action": "splitPane", "split": "none" } },
|
|
{ "command": { "action": "splitPane" } },
|
|
{ "command": { "action": "splitPane", "split": "auto" } },
|
|
{ "command": { "action": "splitPane", "split": "foo" } }
|
|
])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
|
|
// There are only 5 commands here: all of the `"none"`, `"auto"`,
|
|
// `"foo"`, `null`, and <no args> bindings all generate the same action,
|
|
// which will generate just a single name for all of them.
|
|
VERIFY_ARE_EQUAL(5u, commands.Size());
|
|
|
|
{
|
|
auto command = commands.Lookup(L"Split pane");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"Split pane, split: left");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Left, realArgs.SplitDirection());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"Split pane, split: right");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"Split pane, split: up");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Up, realArgs.SplitDirection());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"Split pane, split: down");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
|
|
}
|
|
}
|
|
void CommandTests::TestLayerOnAutogeneratedName()
|
|
{
|
|
const std::string commands0String{ R"([
|
|
{ "command": { "action": "splitPane" } },
|
|
{ "name":"Split pane", "command": { "action": "splitPane", "split": "vertical" } },
|
|
])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
|
|
{
|
|
auto command = commands.Lookup(L"Split pane");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
|
|
}
|
|
}
|
|
|
|
void CommandTests::TestGenerateCommandline()
|
|
{
|
|
const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope;
|
|
|
|
const std::string commands0String{ R"([
|
|
{
|
|
"name":"action0",
|
|
"command": { "action": "newWindow" }
|
|
},
|
|
{
|
|
"name":"action1",
|
|
"command": { "action": "newTab", "profile": "foo" }
|
|
},
|
|
{
|
|
"name":"action2",
|
|
"command": { "action": "newWindow", "profile": "foo" }
|
|
},
|
|
{
|
|
"name":"action3",
|
|
"command": { "action": "newWindow", "commandline": "bar.exe" }
|
|
},
|
|
{
|
|
"name":"action4",
|
|
"command": { "action": "newWindow", "commandline": "pop.exe ya ha ha" }
|
|
},
|
|
{
|
|
"name":"action5",
|
|
"command": { "action": "newWindow", "commandline": "pop.exe \"ya ha ha\"" }
|
|
},
|
|
{
|
|
"name":"action6",
|
|
"command": { "action": "newWindow", "startingDirectory":"C:\\foo", "commandline": "bar.exe" }
|
|
},
|
|
{
|
|
"name":"action7_startingDirectoryWithTrailingSlash",
|
|
"command": { "action": "newWindow", "startingDirectory":"C:\\", "commandline": "bar.exe" }
|
|
},
|
|
{
|
|
"name":"action8_tabTitleEscaping",
|
|
"command": { "action": "newWindow", "tabTitle":"\\\";foo\\" }
|
|
}
|
|
])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(9u, commands.Size());
|
|
|
|
{
|
|
auto command = commands.Lookup(L"action0");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
const auto& terminalArgs = realArgs.TerminalArgs();
|
|
VERIFY_IS_NOT_NULL(terminalArgs);
|
|
auto cmdline = terminalArgs.ToCommandline();
|
|
VERIFY_ARE_EQUAL(L"", cmdline);
|
|
}
|
|
|
|
{
|
|
auto command = commands.Lookup(L"action1");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewTabArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
const auto& terminalArgs = realArgs.TerminalArgs();
|
|
VERIFY_IS_NOT_NULL(terminalArgs);
|
|
auto cmdline = terminalArgs.ToCommandline();
|
|
VERIFY_ARE_EQUAL(L"--profile \"foo\"", cmdline);
|
|
}
|
|
|
|
{
|
|
auto command = commands.Lookup(L"action2");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
const auto& terminalArgs = realArgs.TerminalArgs();
|
|
VERIFY_IS_NOT_NULL(terminalArgs);
|
|
auto cmdline = terminalArgs.ToCommandline();
|
|
VERIFY_ARE_EQUAL(L"--profile \"foo\"", cmdline);
|
|
}
|
|
|
|
{
|
|
auto command = commands.Lookup(L"action3");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
const auto& terminalArgs = realArgs.TerminalArgs();
|
|
VERIFY_IS_NOT_NULL(terminalArgs);
|
|
auto cmdline = terminalArgs.ToCommandline();
|
|
VERIFY_ARE_EQUAL(L"-- \"bar.exe\"", cmdline);
|
|
}
|
|
|
|
{
|
|
auto command = commands.Lookup(L"action4");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
const auto& terminalArgs = realArgs.TerminalArgs();
|
|
VERIFY_IS_NOT_NULL(terminalArgs);
|
|
auto cmdline = terminalArgs.ToCommandline();
|
|
Log::Comment(NoThrowString().Format(
|
|
L"cmdline: \"%s\"", cmdline.c_str()));
|
|
VERIFY_ARE_EQUAL(L"-- \"pop.exe ya ha ha\"", terminalArgs.ToCommandline());
|
|
}
|
|
|
|
{
|
|
auto command = commands.Lookup(L"action5");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
const auto& terminalArgs = realArgs.TerminalArgs();
|
|
VERIFY_IS_NOT_NULL(terminalArgs);
|
|
auto cmdline = terminalArgs.ToCommandline();
|
|
Log::Comment(NoThrowString().Format(
|
|
L"cmdline: \"%s\"", cmdline.c_str()));
|
|
VERIFY_ARE_EQUAL(L"-- \"pop.exe \"ya ha ha\"\"", terminalArgs.ToCommandline());
|
|
}
|
|
|
|
{
|
|
auto command = commands.Lookup(L"action6");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
const auto& terminalArgs = realArgs.TerminalArgs();
|
|
VERIFY_IS_NOT_NULL(terminalArgs);
|
|
auto cmdline = terminalArgs.ToCommandline();
|
|
Log::Comment(NoThrowString().Format(
|
|
L"cmdline: \"%s\"", cmdline.c_str()));
|
|
VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\foo\" -- \"bar.exe\"", terminalArgs.ToCommandline());
|
|
}
|
|
|
|
{
|
|
auto command = commands.Lookup(L"action7_startingDirectoryWithTrailingSlash");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
const auto& terminalArgs = realArgs.TerminalArgs();
|
|
VERIFY_IS_NOT_NULL(terminalArgs);
|
|
auto cmdline = terminalArgs.ToCommandline();
|
|
Log::Comment(NoThrowString().Format(
|
|
L"cmdline: \"%s\"", cmdline.c_str()));
|
|
VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\\\\" -- \"bar.exe\"", terminalArgs.ToCommandline());
|
|
}
|
|
|
|
{
|
|
auto command = commands.Lookup(L"action8_tabTitleEscaping");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
|
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
const auto& terminalArgs = realArgs.TerminalArgs();
|
|
VERIFY_IS_NOT_NULL(terminalArgs);
|
|
auto cmdline = terminalArgs.ToCommandline();
|
|
Log::Comment(NoThrowString().Format(
|
|
L"cmdline: \"%s\"", cmdline.c_str()));
|
|
VERIFY_ARE_EQUAL(LR"-(--title "\\\"\;foo\\")-", terminalArgs.ToCommandline());
|
|
}
|
|
}
|
|
}
|