Migrate FancyZones data persisting from Registry to JSON file (#1194)

* Migrate FancyZones data persisting from Registry to JSON file

* Address PR comment: Remove redundant check

* Addres PR comment: Remove unused Dpi and add CmdArgs enum

* Address PR comment: Make methods const and inline

* Address PR comments: Expose GenerateUniqueId function and use const ref instead of passing wstring by value

* Address PR comment: Use lamdba as callback

* Address PR comment: Move GenerateUniqueId to ZoneWindowUtils namespace

* Address PR comment: Use regular comparison instead of std::wstring::compare

* Address PR comment: Use std::wstring_view for tmp file paths

* Address PR comment: Use scoped lock when accessing member data

* Address PR comment: Remove typedefs to increase code readability

* Address PR comment: removed nullptr checks with corresponding tests

* Address PR comment: Move ZoneSet object instead of copying

* Address PR comment: Make FancyZonesData instance const where possible

* Remove unnecessary gutter variable during calculating zone coordinates

* Remove uneeded subclass

* Avoid unnecessary copying and reserve space for vector if possible

* Save FancyZones data after exiting editor

* App zone history (#18)

* added window and zone set ids to app zone history

* Rename JSON file

* Remove AppZoneHistory migration

* Move parsing of ZoneWindow independent temp files outside of it

* Unit tests update (#19)

* check device existence in map
* updated ZoneSet tests
* updated JsonHelpers tests

* Use single zone count information

* Remove uneeded tests

* Remove one more test

* Remove uneeded line

* Address PR comments - Missing whitespace

* Update zoneset data for new virtual desktops (#21)

* update active zone set with actual data

* Introduce Blank zone set (used to indicate that no layout applied yet). Move parsing completely outside of ZoneWindow.

* Fix unit tests to match modifications in implementation

* Fix applying layouts on startup (second monitor)

Co-authored-by: vldmr11080 <57061786+vldmr11080@users.noreply.github.com>
Co-authored-by: Seraphima <zykovas91@gmail.com>
This commit is contained in:
stefansjfw
2020-02-10 14:59:51 +01:00
committed by GitHub
parent a5e3207715
commit 53f830bb38
41 changed files with 8496 additions and 1905 deletions

View File

@@ -0,0 +1,475 @@
#include "pch.h"
#include <filesystem>
#include <lib/FancyZones.h>
#include <lib/Settings.h>
#include <common/common.h>
#include "util.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace FancyZonesUnitTests
{
TEST_CLASS(FancyZonesUnitTests)
{
HINSTANCE m_hInst;
winrt::com_ptr<IFancyZonesSettings> m_settings;
TEST_METHOD_INITIALIZE(Init)
{
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
m_settings = MakeFancyZonesSettings(m_hInst, L"FancyZonesUnitTests");
Assert::IsTrue(m_settings != nullptr);
}
TEST_METHOD(Create)
{
auto actual = MakeFancyZones(m_hInst, m_settings);
Assert::IsNotNull(actual.get());
}
TEST_METHOD(CreateWithEmptyHinstance)
{
auto actual = MakeFancyZones({}, m_settings);
Assert::IsNotNull(actual.get());
}
TEST_METHOD(CreateWithNullHinstance)
{
auto actual = MakeFancyZones(nullptr, m_settings);
Assert::IsNotNull(actual.get());
}
TEST_METHOD(CreateWithNullSettings)
{
auto actual = MakeFancyZones(m_hInst, nullptr);
Assert::IsNull(actual.get());
}
TEST_METHOD(Run)
{
auto actual = MakeFancyZones(m_hInst, m_settings);
std::vector<std::thread> threads;
std::atomic<int> counter = 0;
const int expectedCount = 10;
auto runFunc = [&]() {
actual->Run();
counter++;
};
for (int i = 0; i < expectedCount; i++)
{
threads.push_back(std::thread(runFunc));
}
for (auto& thread : threads)
{
thread.join();
}
Assert::AreEqual(expectedCount, counter.load());
}
TEST_METHOD(Destroy)
{
auto actual = MakeFancyZones(m_hInst, m_settings);
std::vector<std::thread> threads;
std::atomic<int> counter = 0;
const int expectedCount = 10;
auto destroyFunc = [&]() {
actual->Destroy();
counter++;
};
for (int i = 0; i < expectedCount; i++)
{
threads.push_back(std::thread(destroyFunc));
}
for (auto& thread : threads)
{
thread.join();
}
Assert::AreEqual(expectedCount, counter.load());
}
TEST_METHOD(RunDestroy)
{
auto actual = MakeFancyZones(m_hInst, m_settings);
std::vector<std::thread> threads;
std::atomic<int> counter = 0;
const int expectedCount = 20;
auto func = [&]() {
auto idHash = std::hash<std::thread::id>()(std::this_thread::get_id());
bool run = (idHash % 2 == 0);
run ? actual->Run() : actual->Destroy();
counter++;
};
for (int i = 0; i < expectedCount; i++)
{
threads.push_back(std::thread(func));
}
for (auto& thread : threads)
{
thread.join();
}
Assert::AreEqual(expectedCount, counter.load());
}
};
TEST_CLASS(FancyZonesIZoneWindowHostUnitTests)
{
HINSTANCE m_hInst{};
winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr;
winrt::com_ptr<IZoneWindowHost> m_zoneWindowHost = nullptr;
std::wstring serializedPowerToySettings(const Settings& settings)
{
PowerToysSettings::Settings ptSettings(HINSTANCE{}, L"FancyZonesUnitTests");
ptSettings.add_hotkey(L"fancyzones_editor_hotkey", IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, settings.editorHotkey);
ptSettings.add_bool_toogle(L"fancyzones_shiftDrag", IDS_SETTING_DESCRIPTION_SHIFTDRAG, settings.shiftDrag);
ptSettings.add_bool_toogle(L"fancyzones_overrideSnapHotkeys", IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS, settings.overrideSnapHotkeys);
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_flashZones", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES, settings.zoneSetChange_flashZones);
ptSettings.add_bool_toogle(L"fancyzones_displayChange_moveWindows", IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS, settings.displayChange_moveWindows);
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_moveWindows", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS, settings.zoneSetChange_moveWindows);
ptSettings.add_bool_toogle(L"fancyzones_virtualDesktopChange_moveWindows", IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS, settings.virtualDesktopChange_moveWindows);
ptSettings.add_bool_toogle(L"fancyzones_appLastZone_moveWindows", IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS, settings.appLastZone_moveWindows);
ptSettings.add_bool_toogle(L"use_cursorpos_editor_startupscreen", IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN, settings.use_cursorpos_editor_startupscreen);
ptSettings.add_int_spinner(L"fancyzones_highlight_opacity", IDS_SETTINGS_HIGHLIGHT_OPACITY, settings.zoneHighlightOpacity, 0, 100, 1);
ptSettings.add_color_picker(L"fancyzones_zoneHighlightColor", IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, settings.zoneHightlightColor);
ptSettings.add_multiline_string(L"fancyzones_excluded_apps", IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION, settings.excludedApps);
return ptSettings.serialize();
}
TEST_METHOD_INITIALIZE(Init)
{
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
m_settings = MakeFancyZonesSettings(m_hInst, L"FancyZonesUnitTests");
Assert::IsTrue(m_settings != nullptr);
auto fancyZones = MakeFancyZones(m_hInst, m_settings);
Assert::IsTrue(fancyZones != nullptr);
m_zoneWindowHost = fancyZones.as<IZoneWindowHost>();
Assert::IsTrue(m_zoneWindowHost != nullptr);
}
TEST_METHOD_CLEANUP(Cleanup)
{
const auto settingsFile = PTSettingsHelper::get_module_save_folder_location(L"FancyZonesUnitTests") + L"\\settings.json";
std::filesystem::remove(settingsFile);
}
TEST_METHOD(GetZoneHighlightColor)
{
const auto expected = RGB(171, 175, 238);
const Settings settings{
.shiftDrag = true,
.displayChange_moveWindows = true,
.virtualDesktopChange_moveWindows = true,
.zoneSetChange_flashZones = false,
.zoneSetChange_moveWindows = true,
.overrideSnapHotkeys = false,
.appLastZone_moveWindows = true,
.use_cursorpos_editor_startupscreen = true,
.zoneHightlightColor = L"#abafee",
.zoneHighlightOpacity = 45,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
.excludedApps = L"app\r\napp2",
.excludedAppsArray = { L"APP", L"APP2" },
};
auto config = serializedPowerToySettings(settings);
m_settings->SetConfig(config.c_str());
const auto actual = m_zoneWindowHost->GetZoneHighlightColor();
Assert::AreEqual(expected, actual);
}
TEST_METHOD(GetZoneHighlightOpacity)
{
const auto expected = 88;
const Settings settings{
.shiftDrag = true,
.displayChange_moveWindows = true,
.virtualDesktopChange_moveWindows = true,
.zoneSetChange_flashZones = false,
.zoneSetChange_moveWindows = true,
.overrideSnapHotkeys = false,
.appLastZone_moveWindows = true,
.use_cursorpos_editor_startupscreen = true,
.zoneHightlightColor = L"#abafee",
.zoneHighlightOpacity = expected,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
.excludedApps = L"app\r\napp2",
.excludedAppsArray = { L"APP", L"APP2" },
};
auto config = serializedPowerToySettings(settings);
m_settings->SetConfig(config.c_str());
const auto actual = m_zoneWindowHost->GetZoneHighlightOpacity();
Assert::AreEqual(expected, actual);
}
TEST_METHOD(GetCurrentMonitorZoneSetEmpty)
{
const auto* actual = m_zoneWindowHost->GetParentZoneWindow(Mocks::Monitor());
Assert::IsNull(actual);
}
TEST_METHOD(GetCurrentMonitorZoneSetNullMonitor)
{
const auto* actual = m_zoneWindowHost->GetParentZoneWindow(nullptr);
Assert::IsNull(actual);
}
};
TEST_CLASS(FancyZonesIFancyZonesCallbackUnitTests)
{
HINSTANCE m_hInst{};
winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr;
winrt::com_ptr<IFancyZonesCallback> m_fzCallback = nullptr;
JSONHelpers::FancyZonesData& m_fancyZonesData = JSONHelpers::FancyZonesDataInstance();
std::wstring serializedPowerToySettings(const Settings& settings)
{
PowerToysSettings::Settings ptSettings(HINSTANCE{}, L"FancyZonesUnitTests");
ptSettings.add_hotkey(L"fancyzones_editor_hotkey", IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, settings.editorHotkey);
ptSettings.add_bool_toogle(L"fancyzones_shiftDrag", IDS_SETTING_DESCRIPTION_SHIFTDRAG, settings.shiftDrag);
ptSettings.add_bool_toogle(L"fancyzones_overrideSnapHotkeys", IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS, settings.overrideSnapHotkeys);
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_flashZones", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES, settings.zoneSetChange_flashZones);
ptSettings.add_bool_toogle(L"fancyzones_displayChange_moveWindows", IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS, settings.displayChange_moveWindows);
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_moveWindows", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS, settings.zoneSetChange_moveWindows);
ptSettings.add_bool_toogle(L"fancyzones_virtualDesktopChange_moveWindows", IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS, settings.virtualDesktopChange_moveWindows);
ptSettings.add_bool_toogle(L"fancyzones_appLastZone_moveWindows", IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS, settings.appLastZone_moveWindows);
ptSettings.add_bool_toogle(L"use_cursorpos_editor_startupscreen", IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN, settings.use_cursorpos_editor_startupscreen);
ptSettings.add_int_spinner(L"fancyzones_highlight_opacity", IDS_SETTINGS_HIGHLIGHT_OPACITY, settings.zoneHighlightOpacity, 0, 100, 1);
ptSettings.add_color_picker(L"fancyzones_zoneHighlightColor", IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, settings.zoneHightlightColor);
ptSettings.add_multiline_string(L"fancyzones_excluded_apps", IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION, settings.excludedApps);
return ptSettings.serialize();
}
void sendKeyboardInput(WORD code, bool release = false)
{
INPUT ip;
ip.type = INPUT_KEYBOARD;
ip.ki.wScan = 0; // hardware scan code for key
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;
ip.ki.wVk = code;
ip.ki.dwFlags = release ? KEYEVENTF_KEYUP : 0;
SendInput(1, &ip, sizeof(INPUT));
}
TEST_METHOD_INITIALIZE(Init)
{
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
m_settings = MakeFancyZonesSettings(m_hInst, L"FancyZonesUnitTests");
Assert::IsTrue(m_settings != nullptr);
auto fancyZones = MakeFancyZones(m_hInst, m_settings);
Assert::IsTrue(fancyZones != nullptr);
m_fzCallback = fancyZones.as<IFancyZonesCallback>();
Assert::IsTrue(m_fzCallback != nullptr);
m_fancyZonesData = JSONHelpers::FancyZonesData();
}
TEST_METHOD_CLEANUP(Cleanup)
{
sendKeyboardInput(VK_SHIFT, true);
sendKeyboardInput(VK_LWIN, true);
sendKeyboardInput(VK_CONTROL, true);
const auto settingsFile = PTSettingsHelper::get_module_save_folder_location(L"FancyZonesUnitTests") + L"\\settings.json";
std::filesystem::remove(settingsFile);
}
TEST_METHOD(InMoveSizeTest)
{
Assert::IsFalse(m_fzCallback->InMoveSize());
m_fzCallback->MoveSizeStart(Mocks::Window(), Mocks::Monitor(), POINT{ 0, 0 });
Assert::IsFalse(m_fzCallback->InMoveSize()); //point outside of window rect
const auto window = Mocks::WindowCreate(m_hInst);
const int paddingX = 8, paddingY = 6;
RECT windowRect{};
::GetWindowRect(window, &windowRect);
m_fzCallback->MoveSizeStart(window, Mocks::Monitor(), POINT{ windowRect.left + paddingX, windowRect.top + paddingY });
Assert::IsTrue(m_fzCallback->InMoveSize());
m_fzCallback->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
Assert::IsFalse(m_fzCallback->InMoveSize());
}
TEST_METHOD(OnKeyDownNothingPressed)
{
for (DWORD code = '0'; code <= '9'; code++)
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = code;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_LEFT;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_RIGHT;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
}
TEST_METHOD(OnKeyDownShiftPressed)
{
sendKeyboardInput(VK_SHIFT);
for (DWORD code = '0'; code <= '9'; code++)
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = code;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_LEFT;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_RIGHT;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
}
TEST_METHOD(OnKeyDownWinPressed)
{
sendKeyboardInput(VK_LWIN);
for (DWORD code = '0'; code <= '9'; code++)
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = code;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_LEFT;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_RIGHT;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
}
TEST_METHOD(OnKeyDownWinShiftPressed)
{
sendKeyboardInput(VK_LWIN);
sendKeyboardInput(VK_SHIFT);
for (DWORD code = '0'; code <= '9'; code++)
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = code;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_LEFT;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_RIGHT;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
}
TEST_METHOD(OnKeyDownWinCtrlPressed)
{
sendKeyboardInput(VK_LWIN);
sendKeyboardInput(VK_CONTROL);
const Settings settings{
.overrideSnapHotkeys = false,
};
auto config = serializedPowerToySettings(settings);
m_settings->SetConfig(config.c_str());
for (DWORD code = '0'; code <= '9'; code++)
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = code;
Assert::IsTrue(m_fzCallback->OnKeyDown(&input));
}
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_LEFT;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_RIGHT;
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
}
}
TEST_METHOD(OnKeyDownWinPressedOverride)
{
sendKeyboardInput(VK_LWIN);
const Settings settings{
.overrideSnapHotkeys = true,
};
auto config = serializedPowerToySettings(settings);
m_settings->SetConfig(config.c_str());
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_LEFT;
Assert::IsTrue(m_fzCallback->OnKeyDown(&input));
}
{
tagKBDLLHOOKSTRUCT input{};
input.vkCode = VK_RIGHT;
Assert::IsTrue(m_fzCallback->OnKeyDown(&input));
}
}
};
}

View File

@@ -0,0 +1,714 @@
#include "pch.h"
#include <filesystem>
#include <lib/Settings.h>
#include <lib/FancyZones.h>
#include <common/settings_helpers.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace FancyZonesUnitTests
{
void compareHotkeyObjects(const PowerToysSettings::HotkeyObject& expected, const PowerToysSettings::HotkeyObject& actual)
{
Assert::AreEqual(expected.alt_pressed(), actual.alt_pressed());
Assert::AreEqual(expected.ctrl_pressed(), actual.ctrl_pressed());
Assert::AreEqual(expected.shift_pressed(), actual.shift_pressed());
Assert::AreEqual(expected.win_pressed(), actual.win_pressed());
//NOTE: key_from_code may create different values
//Assert::AreEqual(expected.get_key(), actual.get_key());
Assert::AreEqual(expected.get_code(), actual.get_code());
Assert::AreEqual(expected.get_modifiers(), actual.get_modifiers());
Assert::AreEqual(expected.get_modifiers_repeat(), actual.get_modifiers_repeat());
}
void compareSettings(const Settings& expected, const Settings& actual)
{
Assert::AreEqual(expected.shiftDrag, actual.shiftDrag);
Assert::AreEqual(expected.displayChange_moveWindows, actual.displayChange_moveWindows);
Assert::AreEqual(expected.virtualDesktopChange_moveWindows, actual.virtualDesktopChange_moveWindows);
Assert::AreEqual(expected.zoneSetChange_flashZones, actual.zoneSetChange_flashZones);
Assert::AreEqual(expected.zoneSetChange_moveWindows, actual.zoneSetChange_moveWindows);
Assert::AreEqual(expected.overrideSnapHotkeys, actual.overrideSnapHotkeys);
Assert::AreEqual(expected.appLastZone_moveWindows, actual.appLastZone_moveWindows);
Assert::AreEqual(expected.use_cursorpos_editor_startupscreen, actual.use_cursorpos_editor_startupscreen);
Assert::AreEqual(expected.zoneHightlightColor.c_str(), actual.zoneHightlightColor.c_str());
Assert::AreEqual(expected.zoneHighlightOpacity, actual.zoneHighlightOpacity);
Assert::AreEqual(expected.excludedApps.c_str(), actual.excludedApps.c_str());
Assert::AreEqual(expected.excludedAppsArray.size(), actual.excludedAppsArray.size());
for (int i = 0; i < expected.excludedAppsArray.size(); i++)
{
Assert::AreEqual(expected.excludedAppsArray[i].c_str(), actual.excludedAppsArray[i].c_str());
}
compareHotkeyObjects(expected.editorHotkey, actual.editorHotkey);
}
TEST_CLASS(FancyZonesSettingsCreationUnitTest)
{
HINSTANCE m_hInst;
PCWSTR m_moduleName = L"FancyZonesTest";
std::wstring m_tmpName;
const PowerToysSettings::HotkeyObject m_defaultHotkeyObject = PowerToysSettings::HotkeyObject::from_settings(true, false, false, false, VK_OEM_3);
const Settings m_defaultSettings;
TEST_METHOD_INITIALIZE(Init)
{
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
m_tmpName = PTSettingsHelper::get_module_save_folder_location(m_moduleName) + L"\\settings.json";
}
TEST_METHOD_CLEANUP(Cleanup)
{
std::filesystem::remove(m_tmpName);
}
TEST_METHOD(CreateWithHinstanceDefault)
{
auto actual = MakeFancyZonesSettings({}, m_moduleName);
Assert::IsTrue(actual != nullptr);
auto actualSettings = actual->GetSettings();
compareSettings(m_defaultSettings, actualSettings);
}
TEST_METHOD(CreateWithHinstanceNullptr)
{
auto actual = MakeFancyZonesSettings(nullptr, m_moduleName);
Assert::IsTrue(actual != nullptr);
auto actualSettings = actual->GetSettings();
compareSettings(m_defaultSettings, actualSettings);
}
TEST_METHOD(CreateWithNameEmpty)
{
auto actual = MakeFancyZonesSettings(m_hInst, L"");
Assert::IsTrue(actual != nullptr);
auto actualSettings = actual->GetSettings();
compareSettings(m_defaultSettings, actualSettings);
}
TEST_METHOD(Create)
{
//prepare data
const Settings expected {
.shiftDrag = false,
.displayChange_moveWindows = true,
.virtualDesktopChange_moveWindows = true,
.zoneSetChange_flashZones = true,
.zoneSetChange_moveWindows = true,
.overrideSnapHotkeys = false,
.appLastZone_moveWindows = false,
.use_cursorpos_editor_startupscreen = true,
.zoneHightlightColor = L"#00FFD7",
.zoneHighlightOpacity = 45,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
.excludedApps = L"app",
.excludedAppsArray = { L"APP" },
};
PowerToysSettings::PowerToyValues values(m_moduleName);
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
Assert::IsTrue(actual != nullptr);
auto actualSettings = actual->GetSettings();
compareSettings(expected, actualSettings);
}
TEST_METHOD(CreateWithMultipleApps)
{
//prepare data
const Settings expected {
.shiftDrag = false,
.displayChange_moveWindows = true,
.virtualDesktopChange_moveWindows = true,
.zoneSetChange_flashZones = true,
.zoneSetChange_moveWindows = true,
.overrideSnapHotkeys = false,
.appLastZone_moveWindows = false,
.use_cursorpos_editor_startupscreen = true,
.zoneHightlightColor = L"#00FFD7",
.zoneHighlightOpacity = 45,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
.excludedApps = L"app\r\napp1\r\napp2\r\nanother app",
.excludedAppsArray = { L"APP", L"APP1", L"APP2", L"ANOTHER APP" },
};
PowerToysSettings::PowerToyValues values(m_moduleName);
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
Assert::IsTrue(actual != nullptr);
auto actualSettings = actual->GetSettings();
compareSettings(expected, actualSettings);
}
TEST_METHOD(CreateWithBoolValuesMissed)
{
const Settings expected {
.shiftDrag = m_defaultSettings.shiftDrag,
.displayChange_moveWindows = m_defaultSettings.displayChange_moveWindows,
.virtualDesktopChange_moveWindows = m_defaultSettings.virtualDesktopChange_moveWindows,
.zoneSetChange_flashZones = m_defaultSettings.zoneSetChange_flashZones,
.zoneSetChange_moveWindows = m_defaultSettings.zoneSetChange_moveWindows,
.overrideSnapHotkeys = m_defaultSettings.overrideSnapHotkeys,
.appLastZone_moveWindows = m_defaultSettings.appLastZone_moveWindows,
.use_cursorpos_editor_startupscreen = m_defaultSettings.use_cursorpos_editor_startupscreen,
.zoneHightlightColor = L"#00FFD7",
.zoneHighlightOpacity = 45,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
.excludedApps = L"app",
.excludedAppsArray = { L"APP" },
};
PowerToysSettings::PowerToyValues values(m_moduleName);
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
Assert::IsTrue(actual != nullptr);
auto actualSettings = actual->GetSettings();
compareSettings(expected, actualSettings);
}
TEST_METHOD(CreateColorMissed)
{
//prepare data
const Settings expected {
.shiftDrag = false,
.displayChange_moveWindows = true,
.virtualDesktopChange_moveWindows = true,
.zoneSetChange_flashZones = true,
.zoneSetChange_moveWindows = true,
.overrideSnapHotkeys = false,
.appLastZone_moveWindows = false,
.use_cursorpos_editor_startupscreen = true,
.zoneHightlightColor = m_defaultSettings.zoneHightlightColor,
.zoneHighlightOpacity = 45,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
.excludedApps = L"app",
.excludedAppsArray = { L"APP" },
};
PowerToysSettings::PowerToyValues values(m_moduleName);
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
Assert::IsTrue(actual != nullptr);
auto actualSettings = actual->GetSettings();
compareSettings(expected, actualSettings);
}
TEST_METHOD(CreateOpacityMissed)
{
//prepare data
const Settings expected {
.shiftDrag = false,
.displayChange_moveWindows = true,
.virtualDesktopChange_moveWindows = true,
.zoneSetChange_flashZones = true,
.zoneSetChange_moveWindows = true,
.overrideSnapHotkeys = false,
.appLastZone_moveWindows = false,
.use_cursorpos_editor_startupscreen = true,
.zoneHightlightColor = L"#00FFD7",
.zoneHighlightOpacity = m_defaultSettings.zoneHighlightOpacity,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
.excludedApps = L"app",
.excludedAppsArray = { L"APP" },
};
PowerToysSettings::PowerToyValues values(m_moduleName);
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
Assert::IsTrue(actual != nullptr);
auto actualSettings = actual->GetSettings();
compareSettings(expected, actualSettings);
}
TEST_METHOD(CreateHotkeyMissed)
{
//prepare data
const Settings expected = Settings{
.shiftDrag = false,
.displayChange_moveWindows = true,
.virtualDesktopChange_moveWindows = true,
.zoneSetChange_flashZones = true,
.zoneSetChange_moveWindows = true,
.overrideSnapHotkeys = false,
.appLastZone_moveWindows = false,
.use_cursorpos_editor_startupscreen = true,
.zoneHightlightColor = L"#00FFD7",
.zoneHighlightOpacity = 45,
.editorHotkey = m_defaultSettings.editorHotkey,
.excludedApps = L"app",
.excludedAppsArray = { L"APP" },
};
PowerToysSettings::PowerToyValues values(m_moduleName);
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
Assert::IsTrue(actual != nullptr);
auto actualSettings = actual->GetSettings();
compareSettings(expected, actualSettings);
}
TEST_METHOD(CreateAppsMissed)
{
//prepare data
const Settings expected = Settings{
.shiftDrag = false,
.displayChange_moveWindows = true,
.virtualDesktopChange_moveWindows = true,
.zoneSetChange_flashZones = true,
.zoneSetChange_moveWindows = true,
.overrideSnapHotkeys = false,
.appLastZone_moveWindows = false,
.use_cursorpos_editor_startupscreen = true,
.zoneHightlightColor = L"#00FFD7",
.zoneHighlightOpacity = 45,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
.excludedApps = m_defaultSettings.excludedApps,
.excludedAppsArray = m_defaultSettings.excludedAppsArray,
};
PowerToysSettings::PowerToyValues values(m_moduleName);
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.save_to_settings_file();
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
Assert::IsTrue(actual != nullptr);
auto actualSettings = actual->GetSettings();
compareSettings(expected, actualSettings);
}
TEST_METHOD(CreateWithEmptyJson)
{
json::to_file(m_tmpName, json::JsonObject());
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
Assert::IsTrue(actual != nullptr);
auto actualSettings = actual->GetSettings();
compareSettings(m_defaultSettings, actualSettings);
}
};
TEST_CLASS(FancyZonesSettingsCallbackUnitTests)
{
winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr;
PCWSTR m_moduleName = L"FancyZonesTest";
struct FZCallback : public winrt::implements<FZCallback, IFancyZonesCallback>
{
public:
FZCallback(bool* callFlag) :
m_callFlag(callFlag)
{
*m_callFlag = false;
}
IFACEMETHODIMP_(bool) InMoveSize() noexcept { return false; }
IFACEMETHODIMP_(void) MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept {}
IFACEMETHODIMP_(void) MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept {}
IFACEMETHODIMP_(void) MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept {}
IFACEMETHODIMP_(void) VirtualDesktopChanged() noexcept {}
IFACEMETHODIMP_(void) VirtualDesktopInitialize() noexcept {}
IFACEMETHODIMP_(void) WindowCreated(HWND window) noexcept {}
IFACEMETHODIMP_(bool) OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept { return false; }
IFACEMETHODIMP_(void) ToggleEditor() noexcept
{
Assert::IsNotNull(m_callFlag);
*m_callFlag = true;
}
IFACEMETHODIMP_(void) SettingsChanged() noexcept
{
Assert::IsNotNull(m_callFlag);
*m_callFlag = true;
}
private:
bool* m_callFlag = nullptr;
};
TEST_METHOD_INITIALIZE(Init)
{
HINSTANCE hInst = (HINSTANCE)GetModuleHandleW(nullptr);
const Settings expected{
.shiftDrag = false,
.displayChange_moveWindows = true,
.virtualDesktopChange_moveWindows = true,
.zoneSetChange_flashZones = true,
.zoneSetChange_moveWindows = true,
.overrideSnapHotkeys = false,
.appLastZone_moveWindows = false,
.use_cursorpos_editor_startupscreen = true,
.zoneHightlightColor = L"#00FFD7",
.zoneHighlightOpacity = 45,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
.excludedApps = L"app",
.excludedAppsArray = { L"APP" },
};
PowerToysSettings::PowerToyValues values(m_moduleName);
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
m_settings = MakeFancyZonesSettings(hInst, m_moduleName);
Assert::IsTrue(m_settings != nullptr);
}
TEST_METHOD_CLEANUP(Cleanup)
{
const auto settingsFile = PTSettingsHelper::get_module_save_folder_location(m_moduleName) + L"\\settings.json";
std::filesystem::remove(settingsFile);
}
TEST_METHOD(CallbackSetConfig)
{
bool flag = false;
FZCallback callback(&flag);
json::JsonObject json{};
json.SetNamedValue(L"name", json::JsonValue::CreateStringValue(L"name"));
m_settings->SetCallback(&callback);
m_settings->SetConfig(json.Stringify().c_str());
Assert::IsTrue(flag);
}
TEST_METHOD(CallbackCallCustomAction)
{
bool flag = false;
FZCallback callback(&flag);
json::JsonObject action{};
action.SetNamedValue(L"action_name", json::JsonValue::CreateStringValue(L"ToggledFZEditor"));
m_settings->SetCallback(&callback);
m_settings->CallCustomAction(action.Stringify().c_str());
Assert::IsTrue(flag);
}
TEST_METHOD(CallbackCallCustomActionNotToggle)
{
bool flag = false;
FZCallback callback(&flag);
json::JsonObject action{};
action.SetNamedValue(L"action_name", json::JsonValue::CreateStringValue(L"NOT_ToggledFZEditor"));
m_settings->SetCallback(&callback);
m_settings->CallCustomAction(action.Stringify().c_str());
Assert::IsFalse(flag);
}
TEST_METHOD(CallbackGetConfig)
{
bool flag = false;
FZCallback callback(&flag);
m_settings->SetCallback(&callback);
int bufSize = 0;
m_settings->GetConfig(L"", &bufSize);
Assert::IsFalse(flag);
}
TEST_METHOD(CallbackGetSettings)
{
bool flag = false;
FZCallback callback(&flag);
m_settings->SetCallback(&callback);
m_settings->GetSettings();
Assert::IsFalse(flag);
}
};
TEST_CLASS(FancyZonesSettingsUnitTests)
{
winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr;
PowerToysSettings::Settings* m_ptSettings = nullptr;
PCWSTR m_moduleName = L"FancyZonesTest";
std::wstring serializedPowerToySettings(const Settings& settings)
{
PowerToysSettings::Settings ptSettings(HINSTANCE{}, m_moduleName);
ptSettings.set_description(IDS_SETTING_DESCRIPTION);
ptSettings.set_icon_key(L"pt-fancy-zones");
ptSettings.set_overview_link(L"https://github.com/microsoft/PowerToys/blob/master/src/modules/fancyzones/README.md");
ptSettings.set_video_link(L"https://youtu.be/rTtGzZYAXgY");
ptSettings.add_custom_action(
L"ToggledFZEditor", // action name.
IDS_SETTING_LAUNCH_EDITOR_LABEL,
IDS_SETTING_LAUNCH_EDITOR_BUTTON,
IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION);
ptSettings.add_hotkey(L"fancyzones_editor_hotkey", IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, settings.editorHotkey);
ptSettings.add_bool_toogle(L"fancyzones_shiftDrag", IDS_SETTING_DESCRIPTION_SHIFTDRAG, settings.shiftDrag);
ptSettings.add_bool_toogle(L"fancyzones_overrideSnapHotkeys", IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS, settings.overrideSnapHotkeys);
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_flashZones", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES, settings.zoneSetChange_flashZones);
ptSettings.add_bool_toogle(L"fancyzones_displayChange_moveWindows", IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS, settings.displayChange_moveWindows);
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_moveWindows", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS, settings.zoneSetChange_moveWindows);
ptSettings.add_bool_toogle(L"fancyzones_virtualDesktopChange_moveWindows", IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS, settings.virtualDesktopChange_moveWindows);
ptSettings.add_bool_toogle(L"fancyzones_appLastZone_moveWindows", IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS, settings.appLastZone_moveWindows);
ptSettings.add_bool_toogle(L"use_cursorpos_editor_startupscreen", IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN, settings.use_cursorpos_editor_startupscreen);
ptSettings.add_int_spinner(L"fancyzones_highlight_opacity", IDS_SETTINGS_HIGHLIGHT_OPACITY, settings.zoneHighlightOpacity, 0, 100, 1);
ptSettings.add_color_picker(L"fancyzones_zoneHighlightColor", IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, settings.zoneHightlightColor);
ptSettings.add_multiline_string(L"fancyzones_excluded_apps", IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION, settings.excludedApps);
return ptSettings.serialize();
}
TEST_METHOD_INITIALIZE(Init)
{
HINSTANCE hInst = (HINSTANCE)GetModuleHandleW(nullptr);
//init m_settings
const Settings expected{
.shiftDrag = false,
.displayChange_moveWindows = true,
.virtualDesktopChange_moveWindows = true,
.zoneSetChange_flashZones = true,
.zoneSetChange_moveWindows = true,
.overrideSnapHotkeys = false,
.appLastZone_moveWindows = false,
.use_cursorpos_editor_startupscreen = true,
.zoneHightlightColor = L"#00FFD7",
.zoneHighlightOpacity = 45,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
.excludedApps = L"app",
.excludedAppsArray = { L"APP" },
};
PowerToysSettings::PowerToyValues values(m_moduleName);
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
m_settings = MakeFancyZonesSettings(hInst, m_moduleName);
Assert::IsTrue(m_settings != nullptr);
//init m_ptSettings
m_ptSettings = new PowerToysSettings::Settings(hInst, m_moduleName);
m_ptSettings->set_description(IDS_SETTING_DESCRIPTION);
m_ptSettings->set_icon_key(L"pt-fancy-zones");
m_ptSettings->set_overview_link(L"https://github.com/microsoft/PowerToys/blob/master/src/modules/fancyzones/README.md");
m_ptSettings->set_video_link(L"https://youtu.be/rTtGzZYAXgY");
m_ptSettings->add_custom_action(
L"ToggledFZEditor", // action name.
IDS_SETTING_LAUNCH_EDITOR_LABEL,
IDS_SETTING_LAUNCH_EDITOR_BUTTON,
IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION);
m_ptSettings->add_hotkey(L"fancyzones_editor_hotkey", IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, expected.editorHotkey);
m_ptSettings->add_bool_toogle(L"fancyzones_shiftDrag", IDS_SETTING_DESCRIPTION_SHIFTDRAG, expected.shiftDrag);
m_ptSettings->add_bool_toogle(L"fancyzones_overrideSnapHotkeys", IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS, expected.overrideSnapHotkeys);
m_ptSettings->add_bool_toogle(L"fancyzones_zoneSetChange_flashZones", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES, expected.zoneSetChange_flashZones);
m_ptSettings->add_bool_toogle(L"fancyzones_displayChange_moveWindows", IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS, expected.displayChange_moveWindows);
m_ptSettings->add_bool_toogle(L"fancyzones_zoneSetChange_moveWindows", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS, expected.zoneSetChange_moveWindows);
m_ptSettings->add_bool_toogle(L"fancyzones_virtualDesktopChange_moveWindows", IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS, expected.virtualDesktopChange_moveWindows);
m_ptSettings->add_bool_toogle(L"fancyzones_appLastZone_moveWindows", IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS, expected.appLastZone_moveWindows);
m_ptSettings->add_bool_toogle(L"use_cursorpos_editor_startupscreen", IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN, expected.use_cursorpos_editor_startupscreen);
m_ptSettings->add_int_spinner(L"fancyzones_highlight_opacity", IDS_SETTINGS_HIGHLIGHT_OPACITY, expected.zoneHighlightOpacity, 0, 100, 1);
m_ptSettings->add_color_picker(L"fancyzones_zoneHighlightColor", IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, expected.zoneHightlightColor);
m_ptSettings->add_multiline_string(L"fancyzones_excluded_apps", IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION, expected.excludedApps);
}
TEST_METHOD_CLEANUP(Cleanup)
{
const auto settingsFile = PTSettingsHelper::get_module_save_folder_location(m_moduleName) + L"\\settings.json";
std::filesystem::remove(settingsFile);
}
TEST_METHOD(GetConfig)
{
const int expectedSize = m_ptSettings->serialize().size() + 1;
int actualBufferSize = expectedSize;
PWSTR actualBuffer = new wchar_t[actualBufferSize];
Assert::IsTrue(m_settings->GetConfig(actualBuffer, &actualBufferSize));
Assert::AreEqual(expectedSize, actualBufferSize);
Assert::AreEqual(m_ptSettings->serialize().c_str(), actualBuffer);
}
TEST_METHOD(GetConfigSmallBuffer)
{
const auto serialized = m_ptSettings->serialize();
const int expectedSize = serialized.size() + 1;
int actualBufferSize = m_ptSettings->serialize().size() - 1;
PWSTR actualBuffer = new wchar_t[actualBufferSize];
Assert::IsFalse(m_settings->GetConfig(actualBuffer, &actualBufferSize));
Assert::AreEqual(expectedSize, actualBufferSize);
Assert::AreNotEqual(serialized.c_str(), actualBuffer);
}
TEST_METHOD(GetConfigNullBuffer)
{
const auto serialized = m_ptSettings->serialize();
const int expectedSize = serialized.size() + 1;
int actualBufferSize = 0;
PWSTR actualBuffer = nullptr;
Assert::IsFalse(m_settings->GetConfig(actualBuffer, &actualBufferSize));
Assert::AreEqual(expectedSize, actualBufferSize);
}
TEST_METHOD(SetConfig)
{
//cleanup file before call set config
const auto settingsFile = PTSettingsHelper::get_module_save_folder_location(m_moduleName) + L"\\settings.json";
std::filesystem::remove(settingsFile);
const Settings expected {
.shiftDrag = true,
.displayChange_moveWindows = true,
.virtualDesktopChange_moveWindows = true,
.zoneSetChange_flashZones = false,
.zoneSetChange_moveWindows = true,
.overrideSnapHotkeys = false,
.appLastZone_moveWindows = true,
.use_cursorpos_editor_startupscreen = true,
.zoneHightlightColor = L"#00AABB",
.zoneHighlightOpacity = 45,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
.excludedApps = L"app\r\napp2",
.excludedAppsArray = { L"APP", L"APP2" },
};
auto config = serializedPowerToySettings(expected);
m_settings->SetConfig(config.c_str());
auto actual = m_settings->GetSettings();
compareSettings(expected, actual);
Assert::IsTrue(std::filesystem::exists(settingsFile));
}
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +0,0 @@
#include "pch.h"
#include "lib\RegistryHelpers.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace FancyZonesUnitTests
{
TEST_CLASS(RegistryHelpersUnitTests){
public:
TEST_METHOD(GetDefaultKey){
// Test the path to the key is the same string.
wchar_t key[256];
Assert::AreEqual(0, wcscmp(RegistryHelpers::GetKey(nullptr, key, ARRAYSIZE(key)), L"Software\\SuperFancyZones"));
}
TEST_METHOD(GetKeyWithMonitor)
{
// Test the path to the key is the same string.
wchar_t key[256];
Assert::AreEqual(0, wcscmp(RegistryHelpers::GetKey(L"Monitor1", key, ARRAYSIZE(key)), L"Software\\SuperFancyZones\\Monitor1"));
}
TEST_METHOD(OpenKey)
{
// The default key should exist.
wil::unique_hkey key{ RegistryHelpers::OpenKey({}) };
Assert::IsNotNull(key.get());
// The Monitor1 key shouldn't exist.
wil::unique_hkey key2{ RegistryHelpers::OpenKey(L"Monitor1") };
Assert::IsNull(key2.get());
}
}
;
}

View File

@@ -96,12 +96,15 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="FancyZones.Spec.cpp" />
<ClCompile Include="FancyZonesSettings.Spec.cpp" />
<ClCompile Include="JsonHelpers.Tests.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="RegistryHelpers.Spec.cpp" />
<ClCompile Include="Util.Spec.cpp" />
<ClCompile Include="Util.cpp" />
<ClCompile Include="Zone.Spec.cpp" />
<ClCompile Include="ZoneSet.Spec.cpp" />
<ClCompile Include="ZoneWindow.Spec.cpp" />

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
@@ -24,15 +24,24 @@
<ClCompile Include="Zone.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RegistryHelpers.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Util.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ZoneWindow.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="JsonHelpers.Tests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesSettings.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZones.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">

View File

@@ -0,0 +1,149 @@
#include "pch.h"
#include "Util.h"
static int s_classId = 0;
namespace Mocks
{
class HwndCreator
{
public:
HwndCreator(const std::wstring& title = L"");
~HwndCreator();
HWND operator()(HINSTANCE hInst);
void setHwnd(HWND val);
void setCondition(bool cond);
inline HINSTANCE getHInstance() const { return m_hInst; }
inline const std::wstring& getTitle() const { return m_windowTitle; }
inline const std::wstring& getWindowClassName() const { return m_windowClassName; }
private:
std::wstring m_windowTitle;
std::wstring m_windowClassName;
std::mutex m_mutex;
std::condition_variable m_conditionVar;
bool m_conditionFlag;
HANDLE m_thread;
HINSTANCE m_hInst;
HWND m_hWnd;
};
HWND WindowCreate(HINSTANCE hInst)
{
return HwndCreator()(hInst);
}
}
LRESULT CALLBACK DLLWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
BOOL RegisterDLLWindowClass(LPCWSTR szClassName, Mocks::HwndCreator* creator)
{
if (!creator)
return false;
WNDCLASSEX wc;
wc.hInstance = creator->getHInstance();
wc.lpszClassName = szClassName;
wc.lpfnWndProc = DLLWindowProc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_DBLCLKS;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszMenuName = NULL;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
auto regRes = RegisterClassEx(&wc);
return regRes;
}
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
MSG messages;
Mocks::HwndCreator* creator = reinterpret_cast<Mocks::HwndCreator*>(lpParam);
if (!creator)
return -1;
if (RegisterDLLWindowClass((LPCWSTR)creator->getWindowClassName().c_str(), creator) != 0)
{
auto hWnd = CreateWindowEx(0, (LPCWSTR)creator->getWindowClassName().c_str(), (LPCWSTR)creator->getTitle().c_str(), WS_EX_APPWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, nullptr, nullptr, creator->getHInstance(), NULL);
creator->setHwnd(hWnd);
creator->setCondition(true);
while (GetMessage(&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
creator->setHwnd(hWnd);
}
else
{
creator->setCondition(true);
}
return 1;
}
namespace Mocks
{
HwndCreator::HwndCreator(const std::wstring& title) :
m_windowTitle(title), m_windowClassName(std::to_wstring(++s_classId)), m_conditionFlag(false), m_thread(nullptr), m_hInst(HINSTANCE{}), m_hWnd(nullptr)
{
}
HwndCreator::~HwndCreator()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_conditionVar.wait(lock, [this] { return m_conditionFlag; });
if (m_thread)
{
CloseHandle(m_thread);
}
}
HWND HwndCreator::operator()(HINSTANCE hInst)
{
m_hInst = hInst;
m_conditionFlag = false;
std::unique_lock<std::mutex> lock(m_mutex);
m_thread = CreateThread(0, NULL, ThreadProc, (LPVOID)this, NULL, NULL);
m_conditionVar.wait(lock, [this] { return m_conditionFlag; });
return m_hWnd;
}
void HwndCreator::setHwnd(HWND val)
{
m_hWnd = val;
}
void HwndCreator::setCondition(bool cond)
{
m_conditionFlag = cond;
m_conditionVar.notify_one();
}
}

View File

@@ -1,5 +1,7 @@
#pragma once
#include "lib/JsonHelpers.h"
namespace CustomAssert
{
static void AreEqual(const RECT& r1, const RECT& r2)
@@ -13,9 +15,9 @@ namespace CustomAssert
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(g1 == g2);
}
static void AreEqual(WORD w1, WORD w2)
static void AreEqual(JSONHelpers::ZoneSetLayoutType t1, JSONHelpers::ZoneSetLayoutType t2)
{
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(w1 == w2);
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(t1 == t2);
}
}
@@ -39,4 +41,5 @@ namespace Mocks
return reinterpret_cast<HINSTANCE>(++s_nextInstance);
}
}
HWND WindowCreate(HINSTANCE hInst);
}

View File

@@ -1,5 +1,6 @@
#include "pch.h"
#include "lib\Zone.h"
#include "lib\Settings.h"
#include "Util.h"
@@ -7,48 +8,419 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace FancyZonesUnitTests
{
TEST_CLASS(ZoneUnitTests){
public:
TEST_METHOD(TestCreateZone){
RECT zoneRect{ 10, 10, 200, 200 };
winrt::com_ptr<IZone> zone = MakeZone(zoneRect);
Assert::IsNotNull(&zone);
CustomAssert::AreEqual(zoneRect, zone->GetZoneRect());
TEST_CLASS(ZoneUnitTests)
{
private:
RECT m_zoneRect{ 10, 10, 200, 200 };
HINSTANCE m_hInst{};
constexpr size_t id = 10;
zone->SetId(id);
Assert::AreEqual(zone->Id(), id);
}
HWND addWindow(const winrt::com_ptr<IZone>& zone, bool stamp)
{
HWND window = Mocks::WindowCreate(m_hInst);
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
zone->AddWindowToZone(window, zoneWindow, stamp);
TEST_METHOD(ContainsWindow)
{
RECT zoneRect{ 10, 10, 200, 200 };
winrt::com_ptr<IZone> zone = MakeZone(zoneRect);
HWND newWindow = Mocks::Window();
Assert::IsFalse(zone->ContainsWindow(newWindow));
}
return window;
}
TEST_METHOD(TestAddRemoveWindow)
{
RECT zoneRect{ 10, 10, 200, 200 };
winrt::com_ptr<IZone> zone = MakeZone(zoneRect);
HWND newWindow = Mocks::Window();
void addMany(const winrt::com_ptr<IZone>& zone)
{
for (int i = 0; i < 10; i++)
{
addWindow(zone, i % 2 == 0);
}
}
Assert::IsFalse(zone->ContainsWindow(newWindow));
zone->AddWindowToZone(newWindow, Mocks::Window(), true);
Assert::IsTrue(zone->ContainsWindow(newWindow));
TEST_METHOD_INITIALIZE(Init)
{
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
}
zone->RemoveWindowFromZone(newWindow, false);
Assert::IsFalse(zone->ContainsWindow(newWindow));
}
public:
TEST_METHOD(TestCreateZone)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
Assert::IsNotNull(&zone);
CustomAssert::AreEqual(m_zoneRect, zone->GetZoneRect());
}
TEST_METHOD(TestRemoveInvalidWindow)
{
RECT zoneRect{ 10, 10, 200, 200 };
winrt::com_ptr<IZone> zone = MakeZone(zoneRect);
HWND newWindow = Mocks::Window();
zone->RemoveWindowFromZone(newWindow, false);
}
}
;
TEST_METHOD(TestCreateZoneZeroRect)
{
RECT zoneRect{ 0, 0, 0, 0 };
winrt::com_ptr<IZone> zone = MakeZone(zoneRect);
Assert::IsNotNull(&zone);
CustomAssert::AreEqual(zoneRect, zone->GetZoneRect());
}
TEST_METHOD(GetSetId)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
constexpr size_t id = 10;
zone->SetId(id);
Assert::AreEqual(zone->Id(), id);
}
TEST_METHOD(IsEmpty)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
Assert::IsTrue(zone->IsEmpty());
}
TEST_METHOD(IsNonEmptyStampTrue)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
addWindow(zone, true);
Assert::IsFalse(zone->IsEmpty());
}
TEST_METHOD(IsNonEmptyStampFalse)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
addWindow(zone, false);
Assert::IsFalse(zone->IsEmpty());
}
TEST_METHOD(IsNonEmptyManyWindows)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
for (int i = 0; i < 10; i++)
{
HWND window = Mocks::WindowCreate(m_hInst);
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
}
Assert::IsFalse(zone->IsEmpty());
}
TEST_METHOD(IsNonEmptyManyZoneWindows)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND window = Mocks::WindowCreate(m_hInst);
for (int i = 0; i < 10; i++)
{
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
}
Assert::IsFalse(zone->IsEmpty());
}
TEST_METHOD(IsNonEmptyMany)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
addMany(zone);
Assert::IsFalse(zone->IsEmpty());
}
TEST_METHOD(ContainsWindowEmpty)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND newWindow = Mocks::WindowCreate(m_hInst);
Assert::IsFalse(zone->ContainsWindow(newWindow));
}
TEST_METHOD(ContainsWindowNot)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
addMany(zone);
HWND newWindow = Mocks::WindowCreate(m_hInst);
Assert::IsFalse(zone->ContainsWindow(newWindow));
}
TEST_METHOD(ContainsWindowStampTrue)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND window = addWindow(zone, true);
Assert::IsTrue(zone->ContainsWindow(window));
}
TEST_METHOD(ContainsWindowStampFalse)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND window = addWindow(zone, false);
Assert::IsTrue(zone->ContainsWindow(window));
}
TEST_METHOD(ContainsWindowManyWindows)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
std::vector<HWND> windowVec{};
for (int i = 0; i < 10; i++)
{
HWND window = Mocks::WindowCreate(m_hInst);
windowVec.push_back(window);
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
}
for (auto wnd : windowVec)
{
Assert::IsTrue(zone->ContainsWindow(wnd));
}
}
TEST_METHOD(ContainsWindowManyZoneWindows)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND window = Mocks::WindowCreate(m_hInst);
std::vector<HWND> windowVec{};
for (int i = 0; i < 10; i++)
{
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
windowVec.push_back(window);
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
}
for (auto wnd : windowVec)
{
Assert::IsTrue(zone->ContainsWindow(wnd));
}
}
TEST_METHOD(ContainsWindowMany)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
std::vector<HWND> windowVec{};
for (int i = 0; i < 10; i++)
{
HWND window = addWindow(zone, i % 2 == 0);
windowVec.push_back(window);
}
for (auto wnd : windowVec)
{
Assert::IsTrue(zone->ContainsWindow(wnd));
}
}
TEST_METHOD(AddWindowNullptr)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND window = nullptr;
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
zone->AddWindowToZone(window, zoneWindow, true);
Assert::IsFalse(zone->IsEmpty());
Assert::IsTrue(zone->ContainsWindow(window));
}
TEST_METHOD(AddWindowZoneNullptr)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND window = Mocks::WindowCreate(m_hInst);
HWND zoneWindow = nullptr;
zone->AddWindowToZone(window, zoneWindow, true);
Assert::IsFalse(zone->IsEmpty());
Assert::IsTrue(zone->ContainsWindow(window));
}
TEST_METHOD(AddManySame)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
HWND window = Mocks::WindowCreate(m_hInst);
for (int i = 0; i < 10; i++)
{
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
}
Assert::IsFalse(zone->IsEmpty());
Assert::IsTrue(zone->ContainsWindow(window));
}
TEST_METHOD(AddManySameNullptr)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND zoneWindow = nullptr;
HWND window = nullptr;
for (int i = 0; i < 10; i++)
{
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
}
Assert::IsTrue(zone->ContainsWindow(window));
}
TEST_METHOD(RemoveWindowRestoreSizeTrue)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND newWindow = Mocks::WindowCreate(m_hInst);
zone->AddWindowToZone(newWindow, Mocks::WindowCreate(m_hInst), true);
Assert::IsFalse(zone->IsEmpty());
Assert::IsTrue(zone->ContainsWindow(newWindow));
zone->RemoveWindowFromZone(newWindow, true);
Assert::IsTrue(zone->IsEmpty());
Assert::IsFalse(zone->ContainsWindow(newWindow));
}
TEST_METHOD(RemoveWindowRestoreSizeFalse)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND newWindow = Mocks::WindowCreate(m_hInst);
zone->AddWindowToZone(newWindow, Mocks::WindowCreate(m_hInst), true);
Assert::IsFalse(zone->IsEmpty());
Assert::IsTrue(zone->ContainsWindow(newWindow));
zone->RemoveWindowFromZone(newWindow, false);
Assert::IsTrue(zone->IsEmpty());
Assert::IsFalse(zone->ContainsWindow(newWindow));
}
TEST_METHOD(RemoveInvalidWindowRestoreSizeTrue)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND newWindow = Mocks::WindowCreate(m_hInst);
zone->RemoveWindowFromZone(newWindow, true);
Assert::IsTrue(zone->IsEmpty());
Assert::IsFalse(zone->ContainsWindow(newWindow));
}
TEST_METHOD(RemoveInvalidWindowRestoreSizeFalse)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND newWindow = Mocks::WindowCreate(m_hInst);
zone->RemoveWindowFromZone(newWindow, false);
Assert::IsTrue(zone->IsEmpty());
Assert::IsFalse(zone->ContainsWindow(newWindow));
}
TEST_METHOD(RemoveNullptrWindowRestoreSizeTrue)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND newWindow = nullptr;
zone->AddWindowToZone(newWindow, Mocks::WindowCreate(m_hInst), true);
Assert::IsFalse(zone->IsEmpty());
Assert::IsTrue(zone->ContainsWindow(newWindow));
zone->RemoveWindowFromZone(newWindow, true);
Assert::IsTrue(zone->IsEmpty());
Assert::IsFalse(zone->ContainsWindow(newWindow));
}
TEST_METHOD(RemoveNullptrWindowRestoreSizeFalse)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND newWindow = nullptr;
zone->AddWindowToZone(newWindow, Mocks::WindowCreate(m_hInst), true);
Assert::IsFalse(zone->IsEmpty());
Assert::IsTrue(zone->ContainsWindow(newWindow));
zone->RemoveWindowFromZone(newWindow, false);
Assert::IsTrue(zone->IsEmpty());
Assert::IsFalse(zone->ContainsWindow(newWindow));
}
TEST_METHOD(RemoveMany)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
std::vector<HWND> windowVec{};
for (int i = 0; i < 10; i++)
{
HWND window = addWindow(zone, i % 2 == 0);
windowVec.push_back(window);
}
for (auto wnd : windowVec)
{
zone->RemoveWindowFromZone(wnd, true);
}
Assert::IsTrue(zone->IsEmpty());
}
TEST_METHOD(RemoveManySame)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
HWND window = Mocks::WindowCreate(m_hInst);
for (int i = 0; i < 10; i++)
{
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
}
zone->RemoveWindowFromZone(window, true);
Assert::IsTrue(zone->IsEmpty());
Assert::IsFalse(zone->ContainsWindow(window));
}
TEST_METHOD(RemoveDouble)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
HWND window = Mocks::WindowCreate(m_hInst);
for (int i = 0; i < 10; i++)
{
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
}
zone->RemoveWindowFromZone(window, true);
zone->RemoveWindowFromZone(window, true);
Assert::IsTrue(zone->IsEmpty());
}
TEST_METHOD(StampTrue)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
size_t expected = 123456;
zone->SetId(expected);
HWND window = addWindow(zone, true);
HANDLE actual = GetProp(window, ZONE_STAMP);
Assert::IsNotNull(actual);
size_t actualVal = HandleToLong(actual);
Assert::AreEqual(expected, actualVal);
}
TEST_METHOD(StampTrueNoId)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND window = addWindow(zone, true);
HANDLE actual = GetProp(window, ZONE_STAMP);
Assert::IsNull(actual);
}
TEST_METHOD(StampFalse)
{
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
HWND window = addWindow(zone, false);
HANDLE actual = GetProp(window, ZONE_STAMP);
Assert::IsNull(actual);
}
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,14 @@
#include "pch.h"
#include <filesystem>
#include <common/common.h>
#include <lib/util.h>
#include <lib/ZoneSet.h>
#include <lib/ZoneWindow.h>
#include <lib/FancyZones.h>
#include "Util.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace FancyZonesUnitTests
@@ -17,10 +22,10 @@ namespace FancyZonesUnitTests
{
return RGB(0xFF, 0xFF, 0xFF);
}
IFACEMETHODIMP_(GUID)
GetCurrentMonitorZoneSetId(HMONITOR monitor) noexcept
IFACEMETHODIMP_(IZoneWindow*)
GetParentZoneWindow(HMONITOR monitor) noexcept
{
return m_guid;
return m_zoneWindow;
}
IFACEMETHODIMP_(int)
GetZoneHighlightOpacity() noexcept
@@ -28,47 +33,636 @@ namespace FancyZonesUnitTests
return 100;
}
GUID m_guid;
IZoneWindow* m_zoneWindow;
};
TEST_CLASS(ZoneWindowUnitTests){
public:
TEST_CLASS(ZoneWindowUnitTests)
{
const std::wstring m_deviceId = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}";
const std::wstring m_virtualDesktopId = L"MyVirtualDesktopId";
std::wstringstream m_uniqueId;
TEST_METHOD(TestCreateZoneWindow){
winrt::com_ptr<IZoneWindow> zoneWindow = MakeZoneWindow(nullptr, Mocks::Instance(), Mocks::Monitor(), L"DeviceId", L"MyVirtualDesktopId", false);
Assert::IsNotNull(zoneWindow.get());
}
TEST_METHOD(TestDeviceId)
{
// Window initialization requires a valid HMONITOR - just use the primary for now.
HMONITOR pimaryMonitor = MonitorFromWindow(HWND(), MONITOR_DEFAULTTOPRIMARY);
MockZoneWindowHost host;
std::wstring expectedDeviceId = L"SomeRandomValue";
winrt::com_ptr<IZoneWindow> zoneWindow = MakeZoneWindow(dynamic_cast<IZoneWindowHost*>(&host), Mocks::Instance(), pimaryMonitor, expectedDeviceId.c_str(), L"MyVirtualDesktopId", false);
Assert::AreEqual(expectedDeviceId, zoneWindow->DeviceId());
}
TEST_METHOD(TestUniqueId)
{
// Unique id of the format "ParsedMonitorDeviceId_MonitorWidth_MonitorHeight_VirtualDesktopId
// Example: "DELA026#5&10a58c63&0&UID16777488_1024_768_MyVirtualDesktopId"
std::wstring deviceId(L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}");
// Window initialization requires a valid HMONITOR - just use the primary for now.
HMONITOR pimaryMonitor = MonitorFromWindow(HWND(), MONITOR_DEFAULTTOPRIMARY);
MONITORINFO info;
info.cbSize = sizeof(info);
Assert::IsTrue(GetMonitorInfo(pimaryMonitor, &info));
Rect monitorRect = Rect(info.rcMonitor);
std::wstringstream ss;
ss << L"DELA026#5&10a58c63&0&UID16777488_" << monitorRect.width() << "_" << monitorRect.height() << "_MyVirtualDesktopId";
MockZoneWindowHost host;
winrt::com_ptr<IZoneWindow> zoneWindow = MakeZoneWindow(dynamic_cast<IZoneWindowHost*>(&host), Mocks::Instance(), pimaryMonitor, deviceId.c_str(), L"MyVirtualDesktopId", false);
Assert::AreEqual(zoneWindow->UniqueId().compare(ss.str()), 0);
}
}
;
HINSTANCE m_hInst{};
HMONITOR m_monitor{};
MONITORINFO m_monitorInfo{};
MockZoneWindowHost m_zoneWindowHost{};
IZoneWindowHost* m_hostPtr = m_zoneWindowHost.get_strong().get();
winrt::com_ptr<IZoneWindow> m_zoneWindow;
JSONHelpers::FancyZonesData& m_fancyZonesData = JSONHelpers::FancyZonesDataInstance();
std::wstring GuidString(const GUID& guid)
{
OLECHAR* guidString;
Assert::AreEqual(S_OK, StringFromCLSID(guid, &guidString));
std::wstring guidStr{ guidString };
CoTaskMemFree(guidString);
return guidStr;
}
std::wstring CreateGuidString()
{
GUID guid;
Assert::AreEqual(S_OK, CoCreateGuid(&guid));
return GuidString(guid);
}
TEST_METHOD_INITIALIZE(Init)
{
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
m_monitor = MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
m_monitorInfo.cbSize = sizeof(m_monitorInfo);
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
m_uniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_MyVirtualDesktopId";
Assert::IsFalse(ZoneWindowUtils::GetActiveZoneSetTmpPath().empty());
Assert::IsFalse(ZoneWindowUtils::GetAppliedZoneSetTmpPath().empty());
Assert::IsFalse(ZoneWindowUtils::GetCustomZoneSetsTmpPath().empty());
Assert::IsFalse(std::filesystem::exists(ZoneWindowUtils::GetActiveZoneSetTmpPath()));
Assert::IsFalse(std::filesystem::exists(ZoneWindowUtils::GetAppliedZoneSetTmpPath()));
Assert::IsFalse(std::filesystem::exists(ZoneWindowUtils::GetCustomZoneSetsTmpPath()));
m_fancyZonesData = JSONHelpers::FancyZonesData();
}
TEST_METHOD_CLEANUP(Cleanup)
{
//cleanup temp files if were created
std::filesystem::remove(ZoneWindowUtils::GetActiveZoneSetTmpPath());
std::filesystem::remove(ZoneWindowUtils::GetAppliedZoneSetTmpPath());
std::filesystem::remove(ZoneWindowUtils::GetCustomZoneSetsTmpPath());
m_zoneWindow = nullptr;
}
winrt::com_ptr<IZoneWindow> InitZoneWindowWithActiveZoneSet()
{
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
Assert::IsFalse(std::filesystem::exists(activeZoneSetTempPath));
const auto type = JSONHelpers::ZoneSetLayoutType::Columns;
const auto expectedZoneSet = JSONHelpers::ZoneSetData{ CreateGuidString(), type };
const auto data = JSONHelpers::DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json);
Assert::IsTrue(std::filesystem::exists(activeZoneSetTempPath));
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
return MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
}
void testZoneWindow(winrt::com_ptr<IZoneWindow> zoneWindow)
{
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
Assert::IsNotNull(zoneWindow.get());
Assert::IsFalse(zoneWindow->IsDragEnabled());
Assert::AreEqual(m_uniqueId.str().c_str(), zoneWindow->UniqueId().c_str());
Assert::AreEqual(expectedWorkArea, zoneWindow->WorkAreaKey());
}
public:
TEST_METHOD(CreateZoneWindow)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
}
TEST_METHOD(CreateZoneWindowNoHinst)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), false);
testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
}
TEST_METHOD(CreateZoneWindowNoHinstFlashZones)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), true);
testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
}
TEST_METHOD(CreateZoneWindowNoMonitor)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), false);
Assert::IsNull(m_zoneWindow.get());
Assert::IsNotNull(m_hostPtr);
}
TEST_METHOD(CreateZoneWindowNoMonitorFlashZones)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), true);
Assert::IsNull(m_zoneWindow.get());
Assert::IsNotNull(m_hostPtr);
}
TEST_METHOD(CreateZoneWindowNoDeviceId)
{
// Generate unique id without device id
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, nullptr, m_virtualDesktopId.c_str());
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false);
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
const std::wstring expectedUniqueId = L"FallbackDevice_" + std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom) + L"_" + m_virtualDesktopId;
Assert::IsNotNull(m_zoneWindow.get());
Assert::IsFalse(m_zoneWindow->IsDragEnabled());
Assert::AreEqual(expectedUniqueId.c_str(), m_zoneWindow->UniqueId().c_str());
Assert::AreEqual(expectedWorkArea, m_zoneWindow->WorkAreaKey());
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
}
TEST_METHOD(CreateZoneWindowNoDesktopId)
{
// Generate unique id without virtual desktop id
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, m_deviceId.c_str(), nullptr);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false);
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
Assert::IsNotNull(m_zoneWindow.get());
Assert::IsFalse(m_zoneWindow->IsDragEnabled());
Assert::IsTrue(m_zoneWindow->UniqueId().empty());
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
}
TEST_METHOD(CreateZoneWindowWithActiveZoneTmpFile)
{
using namespace JSONHelpers;
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
{
const auto expectedZoneSet = ZoneSetData{ CreateGuidString(), static_cast<ZoneSetLayoutType>(type) };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
testZoneWindow(actual);
Assert::IsNotNull(actual->ActiveZoneSet());
}
}
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneTmpFile)
{
using namespace JSONHelpers;
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto expectedZoneSet = ZoneSetData{ CreateGuidString(), type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
testZoneWindow(actual);
//custom zone needs temp file for applied zone
Assert::IsNotNull(actual->ActiveZoneSet());
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)0, actualZoneSet.size());
}
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFile)
{
using namespace JSONHelpers;
//save required data
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
const auto appliedZoneSetTempPath = ZoneWindowUtils::GetAppliedZoneSetTmpPath();
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto customSetGuid = CreateGuidString();
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json);
const auto info = CanvasLayoutInfo{
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
};
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
auto customZoneJson = CustomZoneSetJSON::ToJson(CustomZoneSetJSON{ customSetGuid, customZoneData });
json::to_file(appliedZoneSetTempPath, customZoneJson);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
testZoneWindow(actual);
//custom zone needs temp file for applied zone
Assert::IsNotNull(actual->ActiveZoneSet());
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)1, actualZoneSet.size());
}
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFileWithDeletedCustomZones)
{
using namespace JSONHelpers;
//save required data
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
const auto appliedZoneSetTempPath = ZoneWindowUtils::GetAppliedZoneSetTmpPath();
const auto deletedZonesTempPath = ZoneWindowUtils::GetCustomZoneSetsTmpPath();
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto customSetGuid = CreateGuidString();
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json);
const auto info = CanvasLayoutInfo{
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
};
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
const auto customZoneSet = CustomZoneSetJSON{ customSetGuid, customZoneData };
auto customZoneJson = CustomZoneSetJSON::ToJson(customZoneSet);
json::to_file(appliedZoneSetTempPath, customZoneJson);
//save same zone as deleted
json::JsonObject deletedCustomZoneSets = {};
json::JsonArray zonesArray{};
zonesArray.Append(json::JsonValue::CreateStringValue(customZoneSet.uuid.substr(1, customZoneSet.uuid.size() - 2).c_str()));
deletedCustomZoneSets.SetNamedValue(L"deleted-custom-zone-sets", zonesArray);
json::to_file(deletedZonesTempPath, deletedCustomZoneSets);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
m_fancyZonesData.ParseDeletedCustomZoneSetsFromTmpFile(deletedZonesTempPath);
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
testZoneWindow(actual);
Assert::IsNotNull(actual->ActiveZoneSet());
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)1, actualZoneSet.size());
}
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFileWithUnusedDeletedCustomZones)
{
using namespace JSONHelpers;
//save required data
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
const auto appliedZoneSetTempPath = ZoneWindowUtils::GetAppliedZoneSetTmpPath();
const auto deletedZonesTempPath = ZoneWindowUtils::GetCustomZoneSetsTmpPath();
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto customSetGuid = CreateGuidString();
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json);
const auto info = CanvasLayoutInfo{
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
};
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
const auto customZoneSet = CustomZoneSetJSON{ customSetGuid, customZoneData };
auto customZoneJson = CustomZoneSetJSON::ToJson(customZoneSet);
json::to_file(appliedZoneSetTempPath, customZoneJson);
//save different zone as deleted
json::JsonObject deletedCustomZoneSets = {};
json::JsonArray zonesArray{};
const auto uuid = CreateGuidString();
zonesArray.Append(json::JsonValue::CreateStringValue(uuid.substr(1, uuid.size() - 2).c_str()));
deletedCustomZoneSets.SetNamedValue(L"deleted-custom-zone-sets", zonesArray);
json::to_file(deletedZonesTempPath, deletedCustomZoneSets);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
m_fancyZonesData.ParseDeletedCustomZoneSetsFromTmpFile(deletedZonesTempPath);
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
testZoneWindow(actual);
Assert::IsNotNull(actual->ActiveZoneSet());
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)1, actualZoneSet.size());
}
TEST_METHOD(MoveSizeEnter)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeEnter(Mocks::Window(), true);
Assert::AreEqual(expected, actual);
Assert::IsTrue(m_zoneWindow->IsDragEnabled());
}
TEST_METHOD(MoveSizeEnterTwice)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
const auto expected = E_INVALIDARG;
m_zoneWindow->MoveSizeEnter(Mocks::Window(), true);
const auto actual = m_zoneWindow->MoveSizeEnter(Mocks::Window(), false);
Assert::AreEqual(expected, actual);
Assert::IsTrue(m_zoneWindow->IsDragEnabled());
}
TEST_METHOD(MoveSizeUpdate)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ 0, 0 }, true);
Assert::AreEqual(expected, actual);
Assert::IsTrue(m_zoneWindow->IsDragEnabled());
}
TEST_METHOD(MoveSizeUpdatePointNegativeCoordinates)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ -10, -10 }, true);
Assert::AreEqual(expected, actual);
Assert::IsTrue(m_zoneWindow->IsDragEnabled());
}
TEST_METHOD(MoveSizeUpdatePointBigCoordinates)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ m_monitorInfo.rcMonitor.right + 1, m_monitorInfo.rcMonitor.bottom + 1 }, true);
Assert::AreEqual(expected, actual);
Assert::IsTrue(m_zoneWindow->IsDragEnabled());
}
TEST_METHOD(MoveSizeEnd)
{
auto zoneWindow = InitZoneWindowWithActiveZoneSet();
const auto window = Mocks::Window();
zoneWindow->MoveSizeEnter(window, true);
const auto expected = S_OK;
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ 0, 0 });
Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet();
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
Assert::AreNotEqual(-1, actualZoneIndex);
}
TEST_METHOD(MoveSizeEndWindowNotAdded)
{
auto zoneWindow = InitZoneWindowWithActiveZoneSet();
const auto window = Mocks::Window();
zoneWindow->MoveSizeEnter(window, true);
const auto expected = S_OK;
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ 0, 0 });
Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet();
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
Assert::AreEqual(-1, actualZoneIndex);
}
TEST_METHOD(MoveSizeEndDifferentWindows)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
const auto window = Mocks::Window();
m_zoneWindow->MoveSizeEnter(window, true);
const auto expected = E_INVALIDARG;
const auto actual = m_zoneWindow->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
Assert::AreEqual(expected, actual);
}
TEST_METHOD(MoveSizeEndWindowNotSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
const auto expected = E_INVALIDARG;
const auto actual = m_zoneWindow->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
Assert::AreEqual(expected, actual);
}
TEST_METHOD(MoveSizeEndInvalidPoint)
{
auto zoneWindow = InitZoneWindowWithActiveZoneSet();
const auto window = Mocks::Window();
zoneWindow->MoveSizeEnter(window, true);
const auto expected = S_OK;
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ -1, -1 });
Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet();
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
Assert::AreNotEqual(-1, actualZoneIndex); //with invalid point zone remains the same
}
TEST_METHOD(MoveSizeCancel)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeCancel();
Assert::AreEqual(expected, actual);
}
TEST_METHOD(MoveWindowIntoZoneByIndexNoActiveZoneSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
}
TEST_METHOD(MoveWindowIntoZoneByIndex)
{
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
const auto actual = m_zoneWindow->ActiveZoneSet();
}
TEST_METHOD(MoveWindowIntoZoneByDirectionNoActiveZoneSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
}
TEST_METHOD(MoveWindowIntoZoneByDirection)
{
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
const auto window = Mocks::WindowCreate(m_hInst);
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_RIGHT);
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto actual = actualAppZoneHistory.begin()->second;
Assert::AreEqual(0, actual.zoneIndex);
}
TEST_METHOD(MoveWindowIntoZoneByDirectionManyTimes)
{
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
const auto window = Mocks::WindowCreate(m_hInst);
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_RIGHT);
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_RIGHT);
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_RIGHT);
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto actual = actualAppZoneHistory.begin()->second;
Assert::AreEqual(2, actual.zoneIndex);
}
TEST_METHOD(SaveWindowProcessToZoneIndexNoActiveZoneSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->SaveWindowProcessToZoneIndex(Mocks::Window());
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
Assert::IsTrue(actualAppZoneHistory.empty());
}
TEST_METHOD(SaveWindowProcessToZoneIndexNullptrWindow)
{
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->SaveWindowProcessToZoneIndex(nullptr);
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
Assert::IsTrue(actualAppZoneHistory.empty());
}
TEST_METHOD(SaveWindowProcessToZoneIndexNoWindowAdded)
{
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
auto window = Mocks::WindowCreate(m_hInst);
auto zone = MakeZone(RECT{ 0, 0, 100, 100 });
m_zoneWindow->ActiveZoneSet()->AddZone(zone);
m_zoneWindow->SaveWindowProcessToZoneIndex(window);
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
Assert::IsTrue(actualAppZoneHistory.empty());
}
TEST_METHOD(SaveWindowProcessToZoneIndexNoWindowAddedWithFilledAppZoneHistory)
{
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
const auto window = Mocks::WindowCreate(m_hInst);
const auto processPath = get_process_path(window);
const auto deviceId = m_zoneWindow->UniqueId();
const auto zoneSetId = m_zoneWindow->ActiveZoneSet()->Id();
//fill app zone history map
Assert::IsTrue(m_fancyZonesData.SetAppLastZone(window, deviceId, GuidString(zoneSetId), 0));
Assert::AreEqual((size_t)1, m_fancyZonesData.GetAppZoneHistoryMap().size());
Assert::AreEqual(0, m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex);
//add zone without window
const auto zone = MakeZone(RECT{ 0, 0, 100, 100 });
m_zoneWindow->ActiveZoneSet()->AddZone(zone);
m_zoneWindow->SaveWindowProcessToZoneIndex(window);
Assert::AreEqual((size_t)1, m_fancyZonesData.GetAppZoneHistoryMap().size());
Assert::AreEqual(0, m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex);
}
TEST_METHOD(SaveWindowProcessToZoneIndexWindowAdded)
{
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
auto window = Mocks::WindowCreate(m_hInst);
const auto processPath = get_process_path(window);
const auto deviceId = m_zoneWindow->UniqueId();
const auto zoneSetId = m_zoneWindow->ActiveZoneSet()->Id();
auto zone = MakeZone(RECT{ 0, 0, 100, 100 });
zone->AddWindowToZone(window, Mocks::Window(), false);
m_zoneWindow->ActiveZoneSet()->AddZone(zone);
//fill app zone history map
Assert::IsTrue(m_fancyZonesData.SetAppLastZone(window, deviceId, GuidString(zoneSetId), 2));
Assert::AreEqual((size_t)1, m_fancyZonesData.GetAppZoneHistoryMap().size());
Assert::AreEqual(2, m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex);
m_zoneWindow->SaveWindowProcessToZoneIndex(window);
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto expected = m_zoneWindow->ActiveZoneSet()->GetZoneIndexFromWindow(window);
const auto actual = m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex;
Assert::AreEqual(expected, actual);
}
};
}