/*********************************************************************** 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; public: bool capturing = false; GlobalShortcutKeyList globalShortcutKeys; UnitTestRemoteProtocol_IO(const UnitTestScreenConfig& _globalConfig) : UnitTestRemoteProtocolBase(_globalConfig) { } protected: /*********************************************************************** IGuiRemoteProtocolMessages (IO) ***********************************************************************/ void Impl_IOUpdateGlobalShortcutKey(const Ptr& 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 mousePosition; collections::SortedList 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 position = {}) { if (position) MouseMove(position.Value()); auto info = MakeMouseInfo(); info.wheel = up; UseEvents().OnIOVWheel(info); } void _Wheel(vint up, Nullable 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 position = {}) { if (position) MouseMove(position.Value()); auto info = MakeMouseInfo(); info.wheel = right; UseEvents().OnIOHWheel(info); } void _HWheel(vint right, Nullable 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 position = {}) { _Wheel(-jumps * 120, position); } void WheelDown(vint jumps, Nullable position, bool ctrl, bool shift, bool alt) { _Wheel(-jumps * 120, position, ctrl, shift, alt); } void WheelUp(vint jumps = 1, Nullable position = {}) { _Wheel(jumps * 120, position); } void WheelUp(vint jumps, Nullable position, bool ctrl, bool shift, bool alt) { _Wheel(jumps * 120, position, ctrl, shift, alt); } void HWheelLeft(vint jumps = 1, Nullable position = {}) { _HWheel(-jumps * 120, position); } void HWheelLeft(vint jumps, Nullable position, bool ctrl, bool shift, bool alt) { _HWheel(-jumps * 120, position, ctrl, shift, alt); } void HWheelRight(vint jumps = 1, Nullable position = {}) { _HWheel(jumps * 120, position); } void HWheelRight(vint jumps, Nullable 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 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 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 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 position = {})\ {\ _ ## PREFIX ## Down(position);\ _ ## PREFIX ## Up(position);\ }\ void PREFIX ## Click(Nullable 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 position = {})\ {\ _ ## PREFIX ## Down(position);\ _ ## PREFIX ## Up(position);\ _ ## PREFIX ## DBClick(position);\ _ ## PREFIX ## Up(position);\ }\ void PREFIX ## DBClick(Nullable 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 characterLayouts; collections::List lines; Size cachedSize; collections::Dictionary cachedInlineObjectBounds; }; /*********************************************************************** UnitTestRemoteProtocol ***********************************************************************/ using ElementDescVariant = remoteprotocol::UnitTest_ElementDescVariant; using UnitTestRenderingDom = remoteprotocol::RenderingDom; struct UnitTestLoggedFrame { vint frameId; collections::List renderingCommandsLog; Nullable renderingDiffs; Ptr renderingDom; }; using UnitTestLoggedFrameList = collections::List>; class UnitTestRemoteProtocol_Rendering : public virtual UnitTestRemoteProtocolBase { using IdSet = collections::SortedList; using Base64ToImageMetadataMap = collections::Dictionary; using ElementDescMap = collections::Dictionary; using ImageMetadataMap = collections::Dictionary; protected: remoteprotocol::RenderingDomBuilder renderingDomBuilder; remoteprotocol::UnitTest_RenderingTrace loggedTrace; UnitTestLoggedFrameList loggedFrames; bool lastRenderingCommandsOpening = false; Ptr receivedDom; remoteprotocol::DomIndex receivedDomIndex; bool receivedDomDiffMessage = false; bool receivedElementMessage = false; ElementDescMap lastElementDescs; IdSet removedElementIds; IdSet removedImageIds; Ptr cachedImageMetadatas; remoteprotocol::ElementMeasurings measuringForNextRendering; regex::Regex regexCrLf{ L"/n|/r(/n)?" }; collections::Dictionary> paragraphStates; void ResetCreatedObjects() { loggedTrace.createdElements = Ptr(new collections::Dictionary); loggedTrace.imageCreations = Ptr(new remoteprotocol::ArrayMap); loggedTrace.imageMetadatas = Ptr(new remoteprotocol::ArrayMap); lastElementDescs.Clear(); } public: UnitTestRemoteProtocol_Rendering(const UnitTestScreenConfig& _globalConfig) : UnitTestRemoteProtocolBase(_globalConfig) { ResetCreatedObjects(); loggedTrace.frames = Ptr(new collections::List); } protected: /*********************************************************************** IGuiRemoteProtocolMessages (Rendering) ***********************************************************************/ Ptr GetLastRenderingFrame(); Ptr 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 dom); void Impl_RendererRenderDom(const Ptr& arguments); void Impl_RendererRenderDomDiff(const remoteprotocol::RenderingDom_DiffsInOrder& arguments); /*********************************************************************** IGuiRemoteProtocolMessages (Elements) ***********************************************************************/ void Impl_RendererCreated(const Ptr>& arguments); void Impl_RendererDestroyed(const Ptr>& arguments); template 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 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 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); 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, Func>; protected: const UnitTestFrameworkConfig& frameworkConfig; WString appName; collections::List 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 void OnNextIdleFrame(TCallback&& callback) { processRemoteEvents.Add({ Nullable{},std::forward(callback) }); } template void OnNextIdleFrame(const WString& name, TCallback&& callback) { processRemoteEvents.Add({ name,std::forward(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; using UnitTestLinkFunc = vl::Func; } 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 config = {}); extern void GacUIUnitTest_StartAsync(const vl::WString& appName, vl::Nullable config = {}); extern void GacUIUnitTest_Start_WithResourceAsText(const vl::WString& appName, vl::Nullable config, const vl::WString& resourceText); extern vl::Ptr GacUIUnitTest_CompileAndLoad(const vl::WString& xmlResource); #ifdef VCZH_DESCRIPTABLEOBJECT_WITH_METADATA struct GacUIUnitTest_Installer { vl::Func initialize; vl::Func finalize; vl::Func installWindow; vl::Nullable config; }; template 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()); 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