terminal/src/til/ut_til/string.cpp
Leonard Hecker 10b12ac90c
Introduce vk() and sc() key chord specifiers (#10666)
This commit introduces an alternative to specifying key bindings as a combination of key modifiers and a character. It allows you to specify an explicit virtual key as `vk(nnn)`.
Additionally this commit makes it possible to bind actions to scan codes. As scan code 41 appears to be the button below the Escape key on virtually all keyboards, we'll be able to bind the quake mode hotkey to `win+sc(41)` and have it work consistently across most if not all keyboard layouts.

## PR Checklist
* [x] Closes #7539, Closes #10203
* [x] I work here
* [x] Tests added/passed

## Validation Steps Performed

The following was tested both on US and DE keyboard layouts:
* Ctrl+, opens settings ✔️
* Win+` opens quake mode window ✔️
* Ctrl+plus/minus increase/decrease font size ✔️
2021-07-20 22:34:51 +00:00

173 lines
5.5 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
class StringTests
{
TEST_CLASS(StringTests);
TEST_METHOD(VisualizeControlCodes)
{
const std::wstring_view input{ L"\u001b[A \u001b[B\x7f" };
const std::wstring_view expected{ L"\u241b[A\u2423\u241b[B\x2421" };
const auto actual = til::visualize_control_codes(input);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(starts_with)
{
VERIFY_IS_TRUE(til::starts_with("", ""));
VERIFY_IS_TRUE(til::starts_with("abc", ""));
VERIFY_IS_TRUE(til::starts_with("abc", "a"));
VERIFY_IS_TRUE(til::starts_with("abc", "ab"));
VERIFY_IS_TRUE(til::starts_with("abc", "abc"));
VERIFY_IS_FALSE(til::starts_with("abc", "abcd"));
VERIFY_IS_FALSE(til::starts_with("", "abc"));
VERIFY_IS_FALSE(til::starts_with("a", "abc"));
VERIFY_IS_FALSE(til::starts_with("ab", "abc"));
VERIFY_IS_TRUE(til::starts_with("abc", "abc"));
VERIFY_IS_TRUE(til::starts_with("abcd", "abc"));
}
TEST_METHOD(ends_with)
{
VERIFY_IS_TRUE(til::ends_with("", ""));
VERIFY_IS_TRUE(til::ends_with("abc", ""));
VERIFY_IS_TRUE(til::ends_with("abc", "c"));
VERIFY_IS_TRUE(til::ends_with("abc", "bc"));
VERIFY_IS_TRUE(til::ends_with("abc", "abc"));
VERIFY_IS_FALSE(til::ends_with("abc", "0abc"));
VERIFY_IS_FALSE(til::ends_with("", "abc"));
VERIFY_IS_FALSE(til::ends_with("c", "abc"));
VERIFY_IS_FALSE(til::ends_with("bc", "abc"));
VERIFY_IS_TRUE(til::ends_with("abc", "abc"));
VERIFY_IS_TRUE(til::ends_with("0abc", "abc"));
}
// Normally this would be the spot where you'd find a TEST_METHOD(from_wchars).
// I didn't quite trust my coding skills and thus opted to use fuzz-testing.
// The below function was used to test from_wchars for unsafety and conformance with clang's strtoul.
// The test was run as:
// clang++ -fsanitize=address,undefined,fuzzer -std=c++17 file.cpp
// and was run for 20min across 16 jobs in parallel.
#if 0
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
while (size > 0 && (isspace(*data) || *data == '+' || *data == '-'))
{
--size;
++data;
}
if (size == 0 || size > 127)
{
return 0;
}
char narrow_buffer[128];
wchar_t wide_buffer[128];
memcpy(narrow_buffer, data, size);
for (size_t i = 0; i < size; ++i)
{
wide_buffer[i] = data[i];
}
// strtoul requires a null terminator
narrow_buffer[size] = 0;
wide_buffer[size] = 0;
char* end;
const auto expected = strtoul(narrow_buffer, &end, 0);
if (end != narrow_buffer + size || expected >= ULONG_MAX / 16)
{
return 0;
}
const auto actual = from_wchars({ wide_buffer, size });
if (expected != actual)
{
__builtin_trap();
}
return 0;
}
#endif
TEST_METHOD(tolower_ascii)
{
for (wchar_t ch = 0; ch < 128; ++ch)
{
VERIFY_ARE_EQUAL(std::towlower(ch), til::tolower_ascii(ch));
}
}
TEST_METHOD(toupper_ascii)
{
for (wchar_t ch = 0; ch < 128; ++ch)
{
VERIFY_ARE_EQUAL(std::towupper(ch), til::toupper_ascii(ch));
}
}
TEST_METHOD(equals_insensitive_ascii)
{
VERIFY_IS_TRUE(til::equals_insensitive_ascii("", ""));
VERIFY_IS_FALSE(til::equals_insensitive_ascii("", "foo"));
VERIFY_IS_FALSE(til::equals_insensitive_ascii("foo", "fo"));
VERIFY_IS_FALSE(til::equals_insensitive_ascii("fooo", "foo"));
VERIFY_IS_TRUE(til::equals_insensitive_ascii("cOUnterStriKE", "COuntERStRike"));
}
TEST_METHOD(prefix_split)
{
{
std::string_view s{ "" };
VERIFY_ARE_EQUAL("", til::prefix_split(s, ""));
VERIFY_ARE_EQUAL("", s);
}
{
std::string_view s{ "" };
VERIFY_ARE_EQUAL("", til::prefix_split(s, " "));
VERIFY_ARE_EQUAL("", s);
}
{
std::string_view s{ " " };
VERIFY_ARE_EQUAL(" ", til::prefix_split(s, ""));
VERIFY_ARE_EQUAL("", s);
}
{
std::string_view s{ "foo" };
VERIFY_ARE_EQUAL("foo", til::prefix_split(s, ""));
VERIFY_ARE_EQUAL("", s);
}
{
std::string_view s{ "foo bar baz" };
VERIFY_ARE_EQUAL("foo", til::prefix_split(s, " "));
VERIFY_ARE_EQUAL("bar baz", s);
VERIFY_ARE_EQUAL("bar", til::prefix_split(s, " "));
VERIFY_ARE_EQUAL("baz", s);
VERIFY_ARE_EQUAL("baz", til::prefix_split(s, " "));
VERIFY_ARE_EQUAL("", s);
}
{
std::string_view s{ "foo123barbaz123" };
VERIFY_ARE_EQUAL("foo", til::prefix_split(s, "123"));
VERIFY_ARE_EQUAL("barbaz123", s);
VERIFY_ARE_EQUAL("barbaz", til::prefix_split(s, "123"));
VERIFY_ARE_EQUAL("", s);
VERIFY_ARE_EQUAL("", til::prefix_split(s, ""));
VERIFY_ARE_EQUAL("", s);
}
}
};