Files
GacUI/Import/GacUI.UnitTest.h
2026-01-28 18:48:25 -08:00

1221 lines
42 KiB
C++

/***********************************************************************
THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY
DEVELOPER: Zihan Chen(vczh)
***********************************************************************/
#include "GacUI.h"
#include "VlppGlrParser.h"
#include "VlppWorkflowLibrary.h"
#include "VlppReflection.h"
#include "VlppOS.h"
#include "Vlpp.h"
#include "VlppRegex.h"
#include "GacUIReflection.h"
#include "VlppWorkflowCompiler.h"
#include "VlppWorkflowRuntime.h"
/***********************************************************************
.\GUIUNITTESTPROTOCOL_SHARED.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/
#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_SHARED
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_SHARED
namespace vl::presentation::unittest
{
struct WindowStyleConfig
{
WString title;
bool enabled = true;
bool topMost = false;
bool showInTaskBar = true;
bool customFrameMode = false;
bool maximizedBox = true;
bool minimizedBox = true;
bool border = true;
bool sizeBox = true;
bool iconVisible = true;
bool titleBar = true;
bool activated = false;
auto operator<=>(const WindowStyleConfig&) const = default;
};
enum class UnitTestRemoteChannel
{
None,
Sync,
Async,
};
struct UnitTestScreenConfig
{
using FontConfig = vl::presentation::remoteprotocol::FontConfig;
using ScreenConfig = vl::presentation::remoteprotocol::ScreenConfig;
WString executablePath;
NativeMargin customFramePadding;
FontConfig fontConfig;
ScreenConfig screenConfig;
bool useDomDiff = false;
UnitTestRemoteChannel useChannel = UnitTestRemoteChannel::None;
void FastInitialize(vint width, vint height, vint taskBarHeight = 0);
};
class UnitTestRemoteProtocolBase : public Object, protected virtual IGuiRemoteProtocol
{
protected:
IGuiRemoteProtocolEvents* events = nullptr;
UnitTestScreenConfig globalConfig;
public:
UnitTestRemoteProtocolBase(const UnitTestScreenConfig& _globalConfig)
: globalConfig(_globalConfig)
{
}
IGuiRemoteProtocol* GetProtocol()
{
return this;
}
IGuiRemoteProtocolEvents* GetEvents() const
{
return events;
}
const UnitTestScreenConfig& GetGlobalConfig() const
{
return globalConfig;
}
protected:
#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE) void Impl_ ## NAME() { CHECK_FAIL(L"Not Implemented!"); }
#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE) void Impl_ ## NAME(vint id) { CHECK_FAIL(L"Not Implemented!"); }
#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE) void Impl_ ## NAME(const REQUEST& arguments) { CHECK_FAIL(L"Not Implemented!"); }
#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE) void Impl_ ## NAME(vint id, const REQUEST& arguments) { CHECK_FAIL(L"Not Implemented!"); }
#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE)
GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER)
#undef MESSAGE_HANDLER
#undef MESSAGE_REQ_RES
#undef MESSAGE_REQ_NORES
#undef MESSAGE_NOREQ_RES
#undef MESSAGE_NOREQ_NORES
/***********************************************************************
IGuiRemoteProtocol
***********************************************************************/
void Initialize(IGuiRemoteProtocolEvents* _events) override
{
events = _events;
}
WString GetExecutablePath() override
{
return globalConfig.executablePath;
}
};
}
#endif
/***********************************************************************
.\GUIUNITTESTPROTOCOL_IO.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/
#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_IO
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_IO
namespace vl::presentation::unittest
{
class UnitTestRemoteProtocol_IO : public virtual UnitTestRemoteProtocolBase
{
using GlobalShortcutKey = remoteprotocol::GlobalShortcutKey;
using GlobalShortcutKeyList = collections::List<GlobalShortcutKey>;
public:
bool capturing = false;
GlobalShortcutKeyList globalShortcutKeys;
UnitTestRemoteProtocol_IO(const UnitTestScreenConfig& _globalConfig)
: UnitTestRemoteProtocolBase(_globalConfig)
{
}
protected:
/***********************************************************************
IGuiRemoteProtocolMessages (IO)
***********************************************************************/
void Impl_IOUpdateGlobalShortcutKey(const Ptr<GlobalShortcutKeyList>& arguments)
{
if (arguments)
{
CopyFrom(globalShortcutKeys, *arguments.Obj());
}
else
{
globalShortcutKeys.Clear();
}
}
void Impl_IORequireCapture()
{
capturing = true;
}
void Impl_IOReleaseCapture()
{
capturing = false;
}
void Impl_IOIsKeyPressing(vint id, const VKEY& arguments)
{
CHECK_FAIL(L"Not Implemented!");
}
void Impl_IOIsKeyToggled(vint id, const VKEY& arguments)
{
CHECK_FAIL(L"Not Implemented!");
}
};
}
#endif
/***********************************************************************
.\GUIUNITTESTPROTOCOL_IOCOMMANDS.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/
#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_IOCOMMANDS
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_IOCOMMANDS
namespace vl::presentation::unittest
{
/***********************************************************************
UnitTestRemoteProtocol
***********************************************************************/
class UnitTestRemoteProtocol_IOCommands : public virtual UnitTestRemoteProtocolBase
{
protected:
Nullable<NativePoint> mousePosition;
collections::SortedList<VKEY> pressingKeys;
bool leftPressing = false;
bool middlePressing = false;
bool rightPressing = false;
bool capslockToggled = false;
IGuiRemoteProtocolEvents& UseEvents()
{
return *this->GetEvents();
}
bool IsPressing(VKEY key)
{
return pressingKeys.Contains(key);
}
NativeWindowMouseInfo MakeMouseInfo()
{
NativeWindowMouseInfo info;
info.ctrl = IsPressing(VKEY::KEY_CONTROL) || IsPressing(VKEY::KEY_LCONTROL) || IsPressing(VKEY::KEY_RCONTROL);
info.shift = IsPressing(VKEY::KEY_SHIFT) || IsPressing(VKEY::KEY_LSHIFT) || IsPressing(VKEY::KEY_RSHIFT);
info.left = leftPressing;
info.middle = middlePressing;
info.right = rightPressing;
info.x = mousePosition.Value().x.value;
info.y = mousePosition.Value().y.value;
info.wheel = 0;
info.nonClient = false;
return info;
}
NativeWindowKeyInfo MakeKeyInfo(VKEY key, bool autoRepeatKeyDown = false)
{
NativeWindowKeyInfo info;
info.code = key;
info.ctrl = IsPressing(VKEY::KEY_CONTROL) || IsPressing(VKEY::KEY_LCONTROL) || IsPressing(VKEY::KEY_RCONTROL);
info.shift = IsPressing(VKEY::KEY_SHIFT) || IsPressing(VKEY::KEY_LSHIFT) || IsPressing(VKEY::KEY_RSHIFT);
info.alt = IsPressing(VKEY::KEY_MENU) || IsPressing(VKEY::KEY_LMENU) || IsPressing(VKEY::KEY_RMENU);
info.capslock = capslockToggled;
info.autoRepeatKeyDown = autoRepeatKeyDown;
return info;
}
NativeWindowCharInfo MakeCharInfo(wchar_t ch)
{
NativeWindowCharInfo info;
info.code = ch;
info.ctrl = IsPressing(VKEY::KEY_CONTROL) || IsPressing(VKEY::KEY_LCONTROL) || IsPressing(VKEY::KEY_RCONTROL);
info.shift = IsPressing(VKEY::KEY_SHIFT) || IsPressing(VKEY::KEY_LSHIFT) || IsPressing(VKEY::KEY_RSHIFT);
info.alt = IsPressing(VKEY::KEY_MENU) || IsPressing(VKEY::KEY_LMENU) || IsPressing(VKEY::KEY_RMENU);
info.capslock = capslockToggled;
return info;
}
public:
UnitTestRemoteProtocol_IOCommands(const UnitTestScreenConfig& _globalConfig)
: UnitTestRemoteProtocolBase(_globalConfig)
{
}
/***********************************************************************
Helper Functions
***********************************************************************/
NativePoint LocationOf(compositions::GuiGraphicsComposition* composition, double ratioX = 0.5, double ratioY = 0.5, vint offsetX = 0, vint offsetY = 0)
{
INativeWindow* nativeWindow = composition->GetRelatedControlHost()->GetNativeWindow();
Rect bounds = composition->GetGlobalBounds();
NativeRect nativeBounds = { nativeWindow->Convert(bounds.LeftTop()),nativeWindow->Convert(bounds.GetSize()) };
vint x = nativeBounds.x1.value + (vint)(nativeBounds.Width().value * ratioX) + offsetX;
vint y = nativeBounds.y1.value + (vint)(nativeBounds.Height().value * ratioY) + offsetY;
NativePoint windowLocation = nativeWindow->GetBounds().LeftTop();
return { windowLocation.x.value + x,windowLocation.y.value + y };
}
NativePoint LocationOf(controls::GuiControl* control, double ratioX = 0.5, double ratioY = 0.5, vint offsetX = 0, vint offsetY = 0)
{
return LocationOf(control->GetBoundsComposition(), ratioX, ratioY, offsetX, offsetY);
}
#define CLASS_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_IOCommands::"
/***********************************************************************
Keys
***********************************************************************/
void _KeyDown(VKEY key)
{
#define ERROR_MESSAGE_PREFIX CLASS_PREFIX L"_KeyDown(...)#"
CHECK_ERROR(!pressingKeys.Contains(key), ERROR_MESSAGE_PREFIX L"The key is already being pressed.");
pressingKeys.Add(key);
if (key == VKEY::KEY_CAPITAL)
{
capslockToggled = !capslockToggled;
}
UseEvents().OnIOKeyDown(MakeKeyInfo(key, false));
#undef ERROR_MESSAGE_PREFIX
}
void _KeyDownRepeat(VKEY key)
{
#define ERROR_MESSAGE_PREFIX CLASS_PREFIX L"_KeyDownRepeat(...)#"
CHECK_ERROR(pressingKeys.Contains(key), ERROR_MESSAGE_PREFIX L"The key is not being pressed.");
UseEvents().OnIOKeyDown(MakeKeyInfo(key, true));
#undef ERROR_MESSAGE_PREFIX
}
void _KeyUp(VKEY key)
{
#define ERROR_MESSAGE_PREFIX CLASS_PREFIX L"_KeyUp(...)#"
CHECK_ERROR(pressingKeys.Contains(key), ERROR_MESSAGE_PREFIX L"The key is not being pressed.");
pressingKeys.Remove(key);
UseEvents().OnIOKeyUp(MakeKeyInfo(key, false));
#undef ERROR_MESSAGE_PREFIX
}
void KeyPress(VKEY key)
{
_KeyDown(key);
_KeyUp(key);
}
void KeyPress(VKEY key, bool ctrl, bool shift, bool alt)
{
if (ctrl) _KeyDown(VKEY::KEY_CONTROL);
if (shift) _KeyDown(VKEY::KEY_SHIFT);
if (alt) _KeyDown(VKEY::KEY_MENU);
KeyPress(key);
if (alt) _KeyUp(VKEY::KEY_MENU);
if (shift) _KeyUp(VKEY::KEY_SHIFT);
if (ctrl) _KeyUp(VKEY::KEY_CONTROL);
}
// Emits plain IOChar events without synthesizing key presses; expand in future iterations when modifiers are required.
void TypeString(const WString& text)
{
if (text.Length() == 0) return;
for (vint i = 0; i < text.Length(); i++)
{
UseEvents().OnIOChar(MakeCharInfo(text[i]));
}
}
/***********************************************************************
Mouse Move Events
***********************************************************************/
void MouseMove(NativePoint position)
{
if (!mousePosition)
{
UseEvents().OnIOMouseEntered();
goto DO_MOUSE_MOVE;
}
if (mousePosition.Value() == position) return;
DO_MOUSE_MOVE:
mousePosition = position;
UseEvents().OnIOMouseMoving(MakeMouseInfo());
}
NativePoint GetMousePosition()
{
#define ERROR_MESSAGE_PREFIX CLASS_PREFIX L"GetMousePosition()#"
CHECK_ERROR(mousePosition, CLASS_PREFIX L"The mouse position is not set.");
return mousePosition.Value();
#undef ERROR_MESSAGE_PREFIX
}
/***********************************************************************
Mouse Wheel Events
***********************************************************************/
void _Wheel(vint up, Nullable<NativePoint> position = {})
{
if (position) MouseMove(position.Value());
auto info = MakeMouseInfo();
info.wheel = up;
UseEvents().OnIOVWheel(info);
}
void _Wheel(vint up, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
{
if (ctrl) _KeyDown(VKEY::KEY_CONTROL);
if (shift) _KeyDown(VKEY::KEY_SHIFT);
if (alt) _KeyDown(VKEY::KEY_MENU);
_Wheel(up, position);
if (alt) _KeyUp(VKEY::KEY_MENU);
if (shift) _KeyUp(VKEY::KEY_SHIFT);
if (ctrl) _KeyUp(VKEY::KEY_CONTROL);
}
void _HWheel(vint right, Nullable<NativePoint> position = {})
{
if (position) MouseMove(position.Value());
auto info = MakeMouseInfo();
info.wheel = right;
UseEvents().OnIOHWheel(info);
}
void _HWheel(vint right, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
{
if (ctrl) _KeyDown(VKEY::KEY_CONTROL);
if (shift) _KeyDown(VKEY::KEY_SHIFT);
if (alt) _KeyDown(VKEY::KEY_MENU);
_HWheel(right, position);
if (alt) _KeyUp(VKEY::KEY_MENU);
if (shift) _KeyUp(VKEY::KEY_SHIFT);
if (ctrl) _KeyUp(VKEY::KEY_CONTROL);
}
void WheelDown(vint jumps = 1, Nullable<NativePoint> position = {})
{
_Wheel(-jumps * 120, position);
}
void WheelDown(vint jumps, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
{
_Wheel(-jumps * 120, position, ctrl, shift, alt);
}
void WheelUp(vint jumps = 1, Nullable<NativePoint> position = {})
{
_Wheel(jumps * 120, position);
}
void WheelUp(vint jumps, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
{
_Wheel(jumps * 120, position, ctrl, shift, alt);
}
void HWheelLeft(vint jumps = 1, Nullable<NativePoint> position = {})
{
_HWheel(-jumps * 120, position);
}
void HWheelLeft(vint jumps, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
{
_HWheel(-jumps * 120, position, ctrl, shift, alt);
}
void HWheelRight(vint jumps = 1, Nullable<NativePoint> position = {})
{
_HWheel(jumps * 120, position);
}
void HWheelRight(vint jumps, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
{
_HWheel(jumps * 120, position, ctrl, shift, alt);
}
/***********************************************************************
Mouse Click Events
***********************************************************************/
#define DEFINE_MOUSE_ACTIONS(PREFIX, LOWER, UPPER)\
void _ ## PREFIX ## Down(Nullable<NativePoint> position = {})\
{\
if (position) MouseMove(position.Value());\
CHECK_ERROR(!LOWER ## Pressing, CLASS_PREFIX L"_" L ## #PREFIX L"Down(...)#" L"The button should not be being pressed.");\
LOWER ## Pressing = true;\
UseEvents().OnIOButtonDown({ remoteprotocol::IOMouseButton::UPPER,MakeMouseInfo() });\
}\
void _ ## PREFIX ## Up(Nullable<NativePoint> position = {})\
{\
if (position) MouseMove(position.Value());\
CHECK_ERROR(LOWER ## Pressing, CLASS_PREFIX L"_" L ## #PREFIX L"Up(...)#" L"The button should be being pressed.");\
LOWER ## Pressing = false;\
UseEvents().OnIOButtonUp({ remoteprotocol::IOMouseButton::UPPER,MakeMouseInfo() });\
}\
void _ ## PREFIX ## DBClick(Nullable<NativePoint> position = {})\
{\
if (position) MouseMove(position.Value());\
CHECK_ERROR(!LOWER ## Pressing, CLASS_PREFIX L"_" L ## #PREFIX L"DBClick(...)#" L"The button should not be being pressed.");\
LOWER ## Pressing = true;\
UseEvents().OnIOButtonDoubleClick({ remoteprotocol::IOMouseButton::UPPER,MakeMouseInfo() });\
}\
void PREFIX ## Click(Nullable<NativePoint> position = {})\
{\
_ ## PREFIX ## Down(position);\
_ ## PREFIX ## Up(position);\
}\
void PREFIX ## Click(Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)\
{\
if (ctrl) _KeyDown(VKEY::KEY_CONTROL);\
if (shift) _KeyDown(VKEY::KEY_SHIFT);\
if (alt) _KeyDown(VKEY::KEY_MENU);\
PREFIX ## Click(position);\
if (alt) _KeyUp(VKEY::KEY_MENU);\
if (shift) _KeyUp(VKEY::KEY_SHIFT);\
if (ctrl) _KeyUp(VKEY::KEY_CONTROL);\
}\
void PREFIX ## DBClick(Nullable<NativePoint> position = {})\
{\
_ ## PREFIX ## Down(position);\
_ ## PREFIX ## Up(position);\
_ ## PREFIX ## DBClick(position);\
_ ## PREFIX ## Up(position);\
}\
void PREFIX ## DBClick(Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)\
{\
if (ctrl) _KeyDown(VKEY::KEY_CONTROL);\
if (shift) _KeyDown(VKEY::KEY_SHIFT);\
if (alt) _KeyDown(VKEY::KEY_MENU);\
PREFIX ## DBClick(position);\
if (alt) _KeyUp(VKEY::KEY_MENU);\
if (shift) _KeyUp(VKEY::KEY_SHIFT);\
if (ctrl) _KeyUp(VKEY::KEY_CONTROL);\
}\
DEFINE_MOUSE_ACTIONS(L, left, Left);
DEFINE_MOUSE_ACTIONS(M, middle, Middle);
DEFINE_MOUSE_ACTIONS(R, right, Right);
#undef DEFINE_MOUSE_ACTIONS
#undef CLASS_PREFIX
};
}
#endif
/***********************************************************************
.\GUIUNITTESTPROTOCOL_MAINWINDOW.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/
#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_MAINWINDOW
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_MAINWINDOW
namespace vl::presentation::unittest
{
class UnitTestRemoteProtocol_MainWindow : public virtual UnitTestRemoteProtocolBase
{
using WindowSizingConfig = remoteprotocol::WindowSizingConfig;
using WindowShowing = remoteprotocol::WindowShowing;
public:
WindowSizingConfig sizingConfig;
WindowStyleConfig styleConfig;
NativeRect lastRestoredSize;
UnitTestRemoteProtocol_MainWindow(const UnitTestScreenConfig& _globalConfig)
: UnitTestRemoteProtocolBase(_globalConfig)
{
sizingConfig.bounds = { 0,0,0,0 };
sizingConfig.clientBounds = { 0,0,0,0 };
sizingConfig.customFramePadding = this->GetGlobalConfig().customFramePadding;
sizingConfig.sizeState = INativeWindow::Restored;
}
protected:
/***********************************************************************
IGuiRemoteProtocolMessages (Controller)
***********************************************************************/
void Impl_ControllerGetFontConfig(vint id)
{
this->GetEvents()->RespondControllerGetFontConfig(id, this->GetGlobalConfig().fontConfig);
}
void Impl_ControllerGetScreenConfig(vint id)
{
this->GetEvents()->RespondControllerGetScreenConfig(id, this->GetGlobalConfig().screenConfig);
}
/***********************************************************************
IGuiRemoteProtocolMessages (Window)
***********************************************************************/
void Impl_WindowGetBounds(vint id)
{
this->GetEvents()->RespondWindowGetBounds(id, sizingConfig);
}
void Impl_WindowNotifySetTitle(const ::vl::WString& arguments)
{
styleConfig.title = arguments;
}
void Impl_WindowNotifySetEnabled(const bool& arguments)
{
styleConfig.enabled = arguments;
}
void Impl_WindowNotifySetTopMost(const bool& arguments)
{
styleConfig.topMost = arguments;
}
void Impl_WindowNotifySetShowInTaskBar(const bool& arguments)
{
styleConfig.showInTaskBar = arguments;
}
void OnBoundsUpdated()
{
sizingConfig.clientBounds = sizingConfig.bounds;
if (sizingConfig.sizeState == INativeWindow::Restored)
{
lastRestoredSize = sizingConfig.bounds;
}
this->GetEvents()->OnWindowBoundsUpdated(sizingConfig);
}
void Impl_WindowNotifySetBounds(const NativeRect& arguments)
{
sizingConfig.bounds = arguments;
OnBoundsUpdated();
}
void Impl_WindowNotifySetClientSize(const NativeSize& arguments)
{
sizingConfig.bounds = { sizingConfig.bounds.LeftTop(), arguments };
OnBoundsUpdated();
}
void Impl_WindowNotifySetCustomFrameMode(const bool& arguments) { styleConfig.customFrameMode = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
void Impl_WindowNotifySetMaximizedBox(const bool& arguments) { styleConfig.maximizedBox = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
void Impl_WindowNotifySetMinimizedBox(const bool& arguments) { styleConfig.minimizedBox = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
void Impl_WindowNotifySetBorder(const bool& arguments) { styleConfig.border = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
void Impl_WindowNotifySetSizeBox(const bool& arguments) { styleConfig.sizeBox = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
void Impl_WindowNotifySetIconVisible(const bool& arguments) { styleConfig.iconVisible = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
void Impl_WindowNotifySetTitleBar(const bool& arguments) { styleConfig.titleBar = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
void Impl_WindowNotifyActivate() { styleConfig.activated = true; }
void Impl_WindowNotifyMinSize(const NativeSize& arguments) {}
void Impl_WindowNotifyShow(const WindowShowing& arguments)
{
styleConfig.activated = arguments.activate;
if (sizingConfig.sizeState != arguments.sizeState)
{
sizingConfig.sizeState = arguments.sizeState;
switch (arguments.sizeState)
{
case INativeWindow::Maximized:
sizingConfig.bounds = this->GetGlobalConfig().screenConfig.clientBounds;
OnBoundsUpdated();
break;
case INativeWindow::Minimized:
sizingConfig.bounds = NativeRect(
{
this->GetGlobalConfig().screenConfig.bounds.x2,
this->GetGlobalConfig().screenConfig.bounds.y2
},
{ 1,1 }
);
OnBoundsUpdated();
break;
case INativeWindow::Restored:
if (sizingConfig.bounds != lastRestoredSize)
{
sizingConfig.bounds = lastRestoredSize;
OnBoundsUpdated();
}
else
{
this->GetEvents()->OnWindowBoundsUpdated(sizingConfig);
}
break;
}
}
}
};
}
#endif
/***********************************************************************
.\GUIUNITTESTPROTOCOL_RENDERING.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/
#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_RENDERING
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_RENDERING
namespace vl::presentation::unittest
{
/***********************************************************************
DocumentParagraphState
***********************************************************************/
struct DocumentParagraphCharLayout
{
double x; // X position of character start
double width; // Character width
vint lineIndex; // Which line this character belongs to
vint height; // Character height (font size or inline object height)
vint baseline; // Distance from top to baseline (for alignment)
bool isInlineObject; // True if this position is part of an inline object
};
struct DocumentParagraphLineInfo
{
vint startPos; // Text position of line start (inclusive)
vint endPos; // Text position of line end (exclusive)
vint y; // Y coordinate of line top
vint height; // Line height (computed from baseline alignment)
vint baseline; // Line baseline position from line top
vint width; // Line width (for alignment calculation)
};
struct DocumentParagraphState
{
WString text;
bool wrapLine = false;
vint maxWidth = -1;
remoteprotocol::ElementHorizontalAlignment alignment = remoteprotocol::ElementHorizontalAlignment::Left;
elements::DocumentTextRunPropertyMap textRuns;
elements::DocumentInlineObjectRunPropertyMap inlineObjectRuns;
elements::DocumentRunPropertyMap mergedRuns;
collections::List<DocumentParagraphCharLayout> characterLayouts;
collections::List<DocumentParagraphLineInfo> lines;
Size cachedSize;
collections::Dictionary<vint, Rect> cachedInlineObjectBounds;
};
/***********************************************************************
UnitTestRemoteProtocol
***********************************************************************/
using ElementDescVariant = remoteprotocol::UnitTest_ElementDescVariant;
using UnitTestRenderingDom = remoteprotocol::RenderingDom;
struct UnitTestLoggedFrame
{
vint frameId;
collections::List<WString> renderingCommandsLog;
Nullable<remoteprotocol::RenderingDom_DiffsInOrder> renderingDiffs;
Ptr<UnitTestRenderingDom> renderingDom;
};
using UnitTestLoggedFrameList = collections::List<Ptr<UnitTestLoggedFrame>>;
class UnitTestRemoteProtocol_Rendering : public virtual UnitTestRemoteProtocolBase
{
using IdSet = collections::SortedList<vint>;
using Base64ToImageMetadataMap = collections::Dictionary<WString, remoteprotocol::ImageMetadata>;
using ElementDescMap = collections::Dictionary<vint, ElementDescVariant>;
using ImageMetadataMap = collections::Dictionary<vint, remoteprotocol::ImageMetadata>;
protected:
remoteprotocol::RenderingDomBuilder renderingDomBuilder;
remoteprotocol::UnitTest_RenderingTrace loggedTrace;
UnitTestLoggedFrameList loggedFrames;
bool lastRenderingCommandsOpening = false;
Ptr<remoteprotocol::RenderingDom> receivedDom;
remoteprotocol::DomIndex receivedDomIndex;
bool receivedDomDiffMessage = false;
bool receivedElementMessage = false;
ElementDescMap lastElementDescs;
IdSet removedElementIds;
IdSet removedImageIds;
Ptr<Base64ToImageMetadataMap> cachedImageMetadatas;
remoteprotocol::ElementMeasurings measuringForNextRendering;
regex::Regex regexCrLf{ L"/n|/r(/n)?" };
collections::Dictionary<vint, Ptr<DocumentParagraphState>> paragraphStates;
void ResetCreatedObjects()
{
loggedTrace.createdElements = Ptr(new collections::Dictionary<vint, remoteprotocol::RendererType>);
loggedTrace.imageCreations = Ptr(new remoteprotocol::ArrayMap<vint, remoteprotocol::ImageCreation, &remoteprotocol::ImageCreation::id>);
loggedTrace.imageMetadatas = Ptr(new remoteprotocol::ArrayMap<vint, remoteprotocol::ImageMetadata, &remoteprotocol::ImageMetadata::id>);
lastElementDescs.Clear();
}
public:
UnitTestRemoteProtocol_Rendering(const UnitTestScreenConfig& _globalConfig)
: UnitTestRemoteProtocolBase(_globalConfig)
{
ResetCreatedObjects();
loggedTrace.frames = Ptr(new collections::List<remoteprotocol::UnitTest_RenderingFrame>);
}
protected:
/***********************************************************************
IGuiRemoteProtocolMessages (Rendering)
***********************************************************************/
Ptr<UnitTestLoggedFrame> GetLastRenderingFrame();
Ptr<UnitTestLoggedFrame> TryGetLastRenderingFrameAndReset();
void Impl_RendererBeginRendering(const remoteprotocol::ElementBeginRendering& arguments);
void Impl_RendererEndRendering(vint id);
/***********************************************************************
IGuiRemoteProtocolMessages (Rendering - Element)
***********************************************************************/
void Impl_RendererBeginBoundary(const remoteprotocol::ElementBoundary& arguments);
void Impl_RendererEndBoundary();
void EnsureRenderedElement(vint id, Rect bounds);
void Impl_RendererRenderElement(const remoteprotocol::ElementRendering& arguments);
/***********************************************************************
IGuiRemoteProtocolMessages (Rendering - Dom)
***********************************************************************/
void CalculateSolidLabelSizesIfNecessary(Ptr<remoteprotocol::RenderingDom> dom);
void Impl_RendererRenderDom(const Ptr<remoteprotocol::RenderingDom>& arguments);
void Impl_RendererRenderDomDiff(const remoteprotocol::RenderingDom_DiffsInOrder& arguments);
/***********************************************************************
IGuiRemoteProtocolMessages (Elements)
***********************************************************************/
void Impl_RendererCreated(const Ptr<collections::List<remoteprotocol::RendererCreation>>& arguments);
void Impl_RendererDestroyed(const Ptr<collections::List<vint>>& arguments);
template<remoteprotocol::RendererType RendererType, typename TElementDesc>
void RequestRendererUpdateElement(const TElementDesc& arguments, const wchar_t* emWrongId, const wchar_t* emWrongType)
{
vint index = loggedTrace.createdElements->Keys().IndexOf(arguments.id);
CHECK_ERROR(index != -1, emWrongId);
CHECK_ERROR(loggedTrace.createdElements->Values()[index] == RendererType, emWrongType);
lastElementDescs.Set(arguments.id, arguments);
}
void Impl_RendererUpdateElement_SolidBorder(const remoteprotocol::ElementDesc_SolidBorder& arguments);
void Impl_RendererUpdateElement_SinkBorder(const remoteprotocol::ElementDesc_SinkBorder& arguments);
void Impl_RendererUpdateElement_SinkSplitter(const remoteprotocol::ElementDesc_SinkSplitter& arguments);
void Impl_RendererUpdateElement_SolidBackground(const remoteprotocol::ElementDesc_SolidBackground& arguments);
void Impl_RendererUpdateElement_GradientBackground(const remoteprotocol::ElementDesc_GradientBackground& arguments);
void Impl_RendererUpdateElement_InnerShadow(const remoteprotocol::ElementDesc_InnerShadow& arguments);
void Impl_RendererUpdateElement_Polygon(const remoteprotocol::ElementDesc_Polygon& arguments);
/***********************************************************************
IGuiRemoteProtocolMessages (Elements - SolidLabel)
***********************************************************************/
void CalculateSolidLabelSizeIfNecessary(vint width, vint height, const remoteprotocol::ElementDesc_SolidLabel& arguments);
void Impl_RendererUpdateElement_SolidLabel(const remoteprotocol::ElementDesc_SolidLabel& arguments);
/***********************************************************************
IGuiRemoteProtocolMessages (Elements - Image)
***********************************************************************/
WString GetBinaryKeyFromBinary(stream::IStream& binary);
WString GetBinaryKeyFromImage(Ptr<INativeImage> image);
remoteprotocol::ImageMetadata MakeImageMetadata(const remoteprotocol::ImageCreation& arguments);
void Impl_ImageCreated(vint id, const remoteprotocol::ImageCreation& arguments);
void Impl_ImageDestroyed(const vint& arguments);
void Impl_RendererUpdateElement_ImageFrame(const remoteprotocol::ElementDesc_ImageFrame& arguments);
/***********************************************************************
IGuiRemoteProtocolMessages (Elements - Document)
***********************************************************************/
void Impl_RendererUpdateElement_DocumentParagraph(vint id, const remoteprotocol::ElementDesc_DocumentParagraph& arguments);
void Impl_DocumentParagraph_GetCaret(vint id, const remoteprotocol::GetCaretRequest& arguments);
void Impl_DocumentParagraph_GetCaretBounds(vint id, const remoteprotocol::GetCaretBoundsRequest& arguments);
void Impl_DocumentParagraph_GetInlineObjectFromPoint(vint id, const remoteprotocol::GetInlineObjectFromPointRequest & arguments);
void Impl_DocumentParagraph_GetNearestCaretFromTextPos(vint id, const remoteprotocol::GetCaretBoundsRequest& arguments);
void Impl_DocumentParagraph_IsValidCaret(vint id, const remoteprotocol::IsValidCaretRequest& arguments);
void Impl_DocumentParagraph_OpenCaret(const remoteprotocol::OpenCaretRequest& arguments);
void Impl_DocumentParagraph_CloseCaret(const vint& arguments);
};
}
#endif
/***********************************************************************
.\GUIUNITTESTPROTOCOL.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/
#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL
namespace vl::presentation::unittest
{
/***********************************************************************
UnitTestFrameworkConfig
***********************************************************************/
struct UnitTestFrameworkConfig
{
filesystem::FilePath snapshotFolder;
filesystem::FilePath resourceFolder;
};
extern const UnitTestFrameworkConfig& GetUnitTestFrameworkConfig();
/***********************************************************************
UnitTestRemoteProtocol
***********************************************************************/
class UnitTestRemoteProtocolFeatures :
public UnitTestRemoteProtocol_MainWindow,
public UnitTestRemoteProtocol_IO,
public UnitTestRemoteProtocol_Rendering,
public UnitTestRemoteProtocol_IOCommands
{
protected:
bool stopped = false;
bool everRendered = false;
Ptr<UnitTestLoggedFrame> candidateFrame;
vint idlingCounter = 0;
bool LogRenderingResult()
{
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocolFeatures::LogRenderingResult()#"
if (auto lastFrame = this->TryGetLastRenderingFrameAndReset())
{
candidateFrame = lastFrame;
everRendered = true;
}
else if (everRendered)
{
if (candidateFrame)
{
idlingCounter = 0;
auto descs = Ptr(new collections::Dictionary<vint, ElementDescVariant>);
CopyFrom(*descs.Obj(), this->lastElementDescs);
this->loggedTrace.frames->Add({
candidateFrame->frameId,
{},
this->sizingConfig,
descs,
candidateFrame->renderingDom
});
candidateFrame = {};
return true;
}
else
{
idlingCounter++;
if (idlingCounter == 100)
{
CHECK_ERROR(
idlingCounter < 100,
ERROR_MESSAGE_PREFIX L"The last frame didn't trigger UI updating. The action registered by OnNextIdleFrame should always make any element or layout to change."
);
}
}
}
return false;
#undef ERROR_MESSAGE_PREFIX
}
public:
UnitTestRemoteProtocolFeatures(const UnitTestScreenConfig& _globalConfig)
: UnitTestRemoteProtocol_MainWindow(_globalConfig)
, UnitTestRemoteProtocol_IO(_globalConfig)
, UnitTestRemoteProtocol_Rendering(_globalConfig)
, UnitTestRemoteProtocol_IOCommands(_globalConfig)
{
}
const auto& GetLoggedTrace()
{
return this->loggedTrace;
}
const auto& GetLoggedFrames()
{
return this->loggedFrames;
}
/***********************************************************************
IGuiRemoteProtocolMessages (Initialization)
***********************************************************************/
protected:
void Impl_ControllerConnectionEstablished()
{
ResetCreatedObjects();
}
void Impl_ControllerConnectionStopped()
{
stopped = true;
}
};
class UnitTestRemoteProtocol
: public UnitTestRemoteProtocolFeatures
, protected virtual IGuiRemoteEventProcessor
{
using EventPair = collections::Pair<Nullable<WString>, Func<void()>>;
protected:
const UnitTestFrameworkConfig& frameworkConfig;
WString appName;
collections::List<EventPair> processRemoteEvents;
vint lastFrameIndex = -1;
vint nextEventIndex = 0;
bool frameExecuting = false;
public:
UnitTestRemoteProtocol(const WString& _appName, UnitTestScreenConfig _globalConfig)
: UnitTestRemoteProtocolBase(_globalConfig)
, UnitTestRemoteProtocolFeatures(_globalConfig)
, frameworkConfig(GetUnitTestFrameworkConfig())
, appName(_appName)
{
}
template<typename TCallback>
void OnNextIdleFrame(TCallback&& callback)
{
processRemoteEvents.Add({ Nullable<WString>{},std::forward<TCallback&&>(callback) });
}
template<typename TCallback>
void OnNextIdleFrame(const WString& name, TCallback&& callback)
{
processRemoteEvents.Add({ name,std::forward<TCallback&&>(callback) });
}
/***********************************************************************
IGuiRemoteProtocolMessages
***********************************************************************/
#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE) void Request ## NAME() override { UnitTestRemoteProtocolFeatures::Impl_ ## NAME(); }
#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE) void Request ## NAME(vint id) override { UnitTestRemoteProtocolFeatures::Impl_ ## NAME(id); }
#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE) void Request ## NAME(const REQUEST& arguments) override { UnitTestRemoteProtocolFeatures::Impl_ ## NAME(arguments); }
#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE) void Request ## NAME(vint id, const REQUEST& arguments) override { UnitTestRemoteProtocolFeatures::Impl_ ## NAME(id, arguments); }
#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE)
GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER)
#undef MESSAGE_HANDLER
#undef MESSAGE_REQ_RES
#undef MESSAGE_REQ_NORES
#undef MESSAGE_NOREQ_RES
#undef MESSAGE_NOREQ_NORES
/***********************************************************************
IGuiRemoteProtocol
***********************************************************************/
void Submit(bool& disconnected) override
{
// TODO: Failure injection to disconnected
}
void ProcessRemoteEvents() override
{
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol::ProcessRemoteEvents()#"
if (!stopped)
{
if (LogRenderingResult())
{
auto [name, func] = processRemoteEvents[nextEventIndex];
CHECK_ERROR(!frameExecuting, ERROR_MESSAGE_PREFIX L"The action registered by OnNextIdleFrame should not call any blocking function, consider using InvokeInMainThread.");
vl::unittest::UnitTest::PrintMessage(L"Execute idle frame[" + (name ? name.Value() : itow(nextEventIndex)) + L"]", vl::unittest::UnitTest::MessageKind::Info);
CHECK_ERROR(lastFrameIndex != loggedTrace.frames->Count() - 1, ERROR_MESSAGE_PREFIX L"No rendering occured after the last idle frame.");
lastFrameIndex = loggedTrace.frames->Count() - 1;
if (name)
{
auto&& lastFrame = (*loggedTrace.frames.Obj())[loggedTrace.frames->Count() - 1];
lastFrame.frameName = name;
}
frameExecuting = true;
func();
frameExecuting = false;
nextEventIndex++;
}
}
#undef ERROR_MESSAGE_PREFIX
}
IGuiRemoteEventProcessor* GetRemoteEventProcessor() override
{
return this;
}
};
}
#endif
/***********************************************************************
.\GUIUNITTESTUTILITIES.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/
#ifndef VCZH_PRESENTATION_GUIUNITTESTUTILITIES
#define VCZH_PRESENTATION_GUIUNITTESTUTILITIES
namespace vl::presentation::unittest
{
class IUnitTestContext : public virtual Interface
{
public:
};
using UnitTestMainFunc = vl::Func<void(UnitTestRemoteProtocol*, IUnitTestContext*)>;
using UnitTestLinkFunc = vl::Func<void(UnitTestRemoteProtocol*, IUnitTestContext*, const UnitTestMainFunc&)>;
}
extern void GacUIUnitTest_Initialize(const vl::presentation::unittest::UnitTestFrameworkConfig* config);
extern void GacUIUnitTest_Finalize();
extern void GacUIUnitTest_SetGuiMainProxy(const vl::presentation::unittest::UnitTestMainFunc& proxy);
extern void GacUIUnitTest_LinkGuiMainProxy(const vl::presentation::unittest::UnitTestLinkFunc& proxy);
extern void GacUIUnitTest_Start(const vl::WString& appName, vl::Nullable<vl::presentation::unittest::UnitTestScreenConfig> config = {});
extern void GacUIUnitTest_StartAsync(const vl::WString& appName, vl::Nullable<vl::presentation::unittest::UnitTestScreenConfig> config = {});
extern void GacUIUnitTest_Start_WithResourceAsText(const vl::WString& appName, vl::Nullable<vl::presentation::unittest::UnitTestScreenConfig> config, const vl::WString& resourceText);
extern vl::Ptr<vl::presentation::GuiResource> GacUIUnitTest_CompileAndLoad(const vl::WString& xmlResource);
#ifdef VCZH_DESCRIPTABLEOBJECT_WITH_METADATA
struct GacUIUnitTest_Installer
{
vl::Func<void()> initialize;
vl::Func<void()> finalize;
vl::Func<void(vl::presentation::controls::GuiWindow*)> installWindow;
vl::Nullable<vl::presentation::unittest::UnitTestScreenConfig> config;
};
template<typename TTheme>
void GacUIUnitTest_StartFast_WithResourceAsText(
const vl::WString& appName,
const vl::WString& windowTypeFullName,
const vl::WString& resourceText,
GacUIUnitTest_Installer installer = {}
)
{
GacUIUnitTest_LinkGuiMainProxy([=](
vl::presentation::unittest::UnitTestRemoteProtocol* protocol,
vl::presentation::unittest::IUnitTestContext* context,
const vl::presentation::unittest::UnitTestMainFunc& previousMainProxy
)
{
{
vl::presentation::remoteprotocol::ControllerGlobalConfig globalConfig;
#if defined VCZH_WCHAR_UTF16
globalConfig.documentCaretFromEncoding = vl::presentation::remoteprotocol::CharacterEncoding::UTF16;
#elif defined VCZH_WCHAR_UTF32
globalConfig.documentCaretFromEncoding = vl::presentation::remoteprotocol::CharacterEncoding::UTF32;
#endif
protocol->GetEvents()->OnControllerConnect(globalConfig);
}
auto theme = vl::Ptr(new TTheme);
vl::presentation::theme::RegisterTheme(theme);
{
if(installer.initialize)
{
installer.initialize();
}
auto windowValue = vl::reflection::description::Value::Create(windowTypeFullName);
TEST_ASSERT(windowValue.GetRawPtr());
auto window = vl::Ptr(windowValue.GetRawPtr()->SafeAggregationCast<vl::presentation::controls::GuiWindow>());
TEST_ASSERT(window);
if (installer.installWindow)
{
installer.installWindow(window.Obj());
}
window->MoveToScreenCenter();
previousMainProxy(protocol, context);
vl::presentation::controls::GetApplication()->Run(window.Obj());
if (installer.finalize)
{
installer.finalize();
}
}
vl::presentation::theme::UnregisterTheme(theme->Name);
});
GacUIUnitTest_Start_WithResourceAsText(appName, installer.config, resourceText);
}
#endif
#endif