mirror of
https://github.com/vczh-libraries/Release.git
synced 2026-02-06 03:42:11 +08:00
1689 lines
60 KiB
C++
1689 lines
60 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(UnitTestScreenConfig _globalConfig)
|
|
: globalConfig(_globalConfig)
|
|
{
|
|
}
|
|
|
|
IGuiRemoteProtocol* GetProtocol()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
IGuiRemoteProtocolEvents* GetEvents() const
|
|
{
|
|
return events;
|
|
}
|
|
|
|
const UnitTestScreenConfig& GetGlobalConfig() const
|
|
{
|
|
return globalConfig;
|
|
}
|
|
|
|
protected:
|
|
|
|
/***********************************************************************
|
|
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
|
|
{
|
|
template<typename TProtocol>
|
|
class UnitTestRemoteProtocol_IO : public TProtocol
|
|
{
|
|
using GlobalShortcutKey = remoteprotocol::GlobalShortcutKey;
|
|
using GlobalShortcutKeyList = collections::List<GlobalShortcutKey>;
|
|
public:
|
|
bool capturing = false;
|
|
GlobalShortcutKeyList globalShortcutKeys;
|
|
|
|
template<typename ...TArgs>
|
|
UnitTestRemoteProtocol_IO(TArgs&& ...args)
|
|
: TProtocol(std::forward<TArgs&&>(args)...)
|
|
{
|
|
}
|
|
|
|
protected:
|
|
|
|
/***********************************************************************
|
|
IGuiRemoteProtocolMessages (IO)
|
|
***********************************************************************/
|
|
|
|
void RequestIOUpdateGlobalShortcutKey(const Ptr<GlobalShortcutKeyList>& arguments) override
|
|
{
|
|
if (arguments)
|
|
{
|
|
CopyFrom(globalShortcutKeys, *arguments.Obj());
|
|
}
|
|
else
|
|
{
|
|
globalShortcutKeys.Clear();
|
|
}
|
|
}
|
|
|
|
void RequestIORequireCapture() override
|
|
{
|
|
capturing = true;
|
|
}
|
|
|
|
void RequestIOReleaseCapture() override
|
|
{
|
|
capturing = false;
|
|
}
|
|
|
|
void RequestIOIsKeyPressing(vint id, const VKEY& arguments) override
|
|
{
|
|
CHECK_FAIL(L"Not Implemented!");
|
|
}
|
|
|
|
void RequestIOIsKeyToggled(vint id, const VKEY& arguments) override
|
|
{
|
|
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
|
|
***********************************************************************/
|
|
|
|
template<typename TProtocol>
|
|
class UnitTestRemoteProtocol_IOCommands : public TProtocol
|
|
{
|
|
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;
|
|
}
|
|
|
|
public:
|
|
|
|
template<typename ...TArgs>
|
|
UnitTestRemoteProtocol_IOCommands(TArgs&& ...args)
|
|
: TProtocol(std::forward<TArgs&&>(args)...)
|
|
{
|
|
}
|
|
|
|
/***********************************************************************
|
|
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<TProtocol>::"
|
|
|
|
/***********************************************************************
|
|
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);
|
|
}
|
|
|
|
/***********************************************************************
|
|
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
|
|
{
|
|
template<typename TProtocol>
|
|
class UnitTestRemoteProtocol_MainWindow : public TProtocol
|
|
{
|
|
using WindowSizingConfig = remoteprotocol::WindowSizingConfig;
|
|
using WindowShowing = remoteprotocol::WindowShowing;
|
|
public:
|
|
WindowSizingConfig sizingConfig;
|
|
WindowStyleConfig styleConfig;
|
|
NativeRect lastRestoredSize;
|
|
|
|
template<typename ...TArgs>
|
|
UnitTestRemoteProtocol_MainWindow(TArgs&& ...args)
|
|
: TProtocol(std::forward<TArgs&&>(args)...)
|
|
{
|
|
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 RequestControllerGetFontConfig(vint id) override
|
|
{
|
|
this->GetEvents()->RespondControllerGetFontConfig(id, this->GetGlobalConfig().fontConfig);
|
|
}
|
|
|
|
void RequestControllerGetScreenConfig(vint id) override
|
|
{
|
|
this->GetEvents()->RespondControllerGetScreenConfig(id, this->GetGlobalConfig().screenConfig);
|
|
}
|
|
|
|
/***********************************************************************
|
|
IGuiRemoteProtocolMessages (Window)
|
|
***********************************************************************/
|
|
|
|
void RequestWindowGetBounds(vint id) override
|
|
{
|
|
this->GetEvents()->RespondWindowGetBounds(id, sizingConfig);
|
|
}
|
|
|
|
void RequestWindowNotifySetTitle(const ::vl::WString& arguments) override
|
|
{
|
|
styleConfig.title = arguments;
|
|
}
|
|
|
|
void RequestWindowNotifySetEnabled(const bool& arguments) override
|
|
{
|
|
styleConfig.enabled = arguments;
|
|
}
|
|
|
|
void RequestWindowNotifySetTopMost(const bool& arguments) override
|
|
{
|
|
styleConfig.topMost = arguments;
|
|
}
|
|
|
|
void RequestWindowNotifySetShowInTaskBar(const bool& arguments) override
|
|
{
|
|
styleConfig.showInTaskBar = arguments;
|
|
}
|
|
|
|
void OnBoundsUpdated()
|
|
{
|
|
sizingConfig.clientBounds = sizingConfig.bounds;
|
|
if (sizingConfig.sizeState == INativeWindow::Restored)
|
|
{
|
|
lastRestoredSize = sizingConfig.bounds;
|
|
}
|
|
this->GetEvents()->OnWindowBoundsUpdated(sizingConfig);
|
|
}
|
|
|
|
void RequestWindowNotifySetBounds(const NativeRect& arguments) override
|
|
{
|
|
sizingConfig.bounds = arguments;
|
|
OnBoundsUpdated();
|
|
}
|
|
|
|
void RequestWindowNotifySetClientSize(const NativeSize& arguments) override
|
|
{
|
|
sizingConfig.bounds = { sizingConfig.bounds.LeftTop(), arguments };
|
|
OnBoundsUpdated();
|
|
}
|
|
|
|
void RequestWindowNotifySetCustomFrameMode(const bool& arguments) override { styleConfig.customFrameMode = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
|
|
void RequestWindowNotifySetMaximizedBox(const bool& arguments) override { styleConfig.maximizedBox = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
|
|
void RequestWindowNotifySetMinimizedBox(const bool& arguments) override { styleConfig.minimizedBox = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
|
|
void RequestWindowNotifySetBorder(const bool& arguments) override { styleConfig.border = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
|
|
void RequestWindowNotifySetSizeBox(const bool& arguments) override { styleConfig.sizeBox = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
|
|
void RequestWindowNotifySetIconVisible(const bool& arguments) override { styleConfig.iconVisible = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
|
|
void RequestWindowNotifySetTitleBar(const bool& arguments) override { styleConfig.titleBar = arguments; this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
|
|
void RequestWindowNotifyActivate() override { styleConfig.activated = true; }
|
|
|
|
void RequestWindowNotifyShow(const WindowShowing& arguments) override
|
|
{
|
|
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
|
|
{
|
|
|
|
/***********************************************************************
|
|
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>>;
|
|
|
|
template<typename TProtocol>
|
|
class UnitTestRemoteProtocol_Rendering : public TProtocol
|
|
{
|
|
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)?" };
|
|
|
|
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:
|
|
|
|
template<typename ...TArgs>
|
|
UnitTestRemoteProtocol_Rendering(TArgs&& ...args)
|
|
: TProtocol(std::forward<TArgs&&>(args)...)
|
|
{
|
|
ResetCreatedObjects();
|
|
loggedTrace.frames = Ptr(new collections::List<remoteprotocol::UnitTest_RenderingFrame>);
|
|
}
|
|
|
|
protected:
|
|
|
|
/***********************************************************************
|
|
IGuiRemoteProtocolMessages (Rendering)
|
|
***********************************************************************/
|
|
|
|
Ptr<UnitTestLoggedFrame> GetLastRenderingFrame()
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::GetLastRenderingCommands()#"
|
|
CHECK_ERROR(lastRenderingCommandsOpening, ERROR_MESSAGE_PREFIX L"The latest frame of commands is not accepting new commands.");
|
|
return loggedFrames[loggedFrames.Count() - 1];
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
Ptr<UnitTestLoggedFrame> TryGetLastRenderingFrameAndReset()
|
|
{
|
|
if (loggedFrames.Count() == 0) return nullptr;
|
|
if (!lastRenderingCommandsOpening) return nullptr;
|
|
lastRenderingCommandsOpening = false;
|
|
return loggedFrames[loggedFrames.Count() - 1];
|
|
}
|
|
|
|
void RequestRendererBeginRendering(const remoteprotocol::ElementBeginRendering& arguments) override
|
|
{
|
|
receivedDomDiffMessage = false;
|
|
receivedElementMessage = false;
|
|
lastRenderingCommandsOpening = true;
|
|
auto frame = Ptr(new UnitTestLoggedFrame);
|
|
frame->frameId = arguments.frameId;
|
|
loggedFrames.Add(frame);
|
|
}
|
|
|
|
void RequestRendererEndRendering(vint id) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::RequestRendererEndRendering(vint)#"
|
|
CHECK_ERROR(receivedElementMessage || receivedDomDiffMessage, ERROR_MESSAGE_PREFIX L"Either dom-diff or element message should be sent before this message.");
|
|
|
|
auto lastFrame = GetLastRenderingFrame();
|
|
if (receivedElementMessage)
|
|
{
|
|
lastFrame->renderingDom = renderingDomBuilder.RequestRendererEndRendering();
|
|
}
|
|
if (receivedDomDiffMessage)
|
|
{
|
|
// receivedDom will be updated in RequestRendererRenderDomDiff
|
|
// store a copy to log
|
|
lastFrame->renderingDom = CopyDom(receivedDom);
|
|
}
|
|
this->GetEvents()->RespondRendererEndRendering(id, measuringForNextRendering);
|
|
measuringForNextRendering = {};
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
/***********************************************************************
|
|
IGuiRemoteProtocolMessages (Rendering - Element)
|
|
***********************************************************************/
|
|
|
|
void RequestRendererBeginBoundary(const remoteprotocol::ElementBoundary& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::RequestRendererBeginBoundary(const ElementBoundary&)#"
|
|
CHECK_ERROR(!receivedDomDiffMessage, ERROR_MESSAGE_PREFIX L"This message could not be used with dom-diff messages in the same rendering cycle.");
|
|
if (!receivedElementMessage)
|
|
{
|
|
renderingDomBuilder.RequestRendererBeginRendering();
|
|
receivedElementMessage = true;
|
|
}
|
|
|
|
renderingDomBuilder.RequestRendererBeginBoundary(arguments);
|
|
|
|
glr::json::JsonFormatting formatting;
|
|
formatting.spaceAfterColon = true;
|
|
formatting.spaceAfterComma = true;
|
|
formatting.crlf = false;
|
|
formatting.compact = true;
|
|
|
|
GetLastRenderingFrame()->renderingCommandsLog.Add(L"RequestRendererBeginBoundary: " + stream::GenerateToStream([&](stream::TextWriter& writer)
|
|
{
|
|
auto jsonLog = remoteprotocol::ConvertCustomTypeToJson(arguments);
|
|
writer.WriteString(glr::json::JsonToString(jsonLog, formatting));
|
|
}));
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererEndBoundary() override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::RequestRendererEndBoundary()#"
|
|
CHECK_ERROR(!receivedDomDiffMessage, ERROR_MESSAGE_PREFIX L"This message could not be used with dom-diff messages in the same rendering cycle.");
|
|
if (!receivedElementMessage)
|
|
{
|
|
renderingDomBuilder.RequestRendererBeginRendering();
|
|
receivedElementMessage = true;
|
|
}
|
|
|
|
renderingDomBuilder.RequestRendererEndBoundary();
|
|
GetLastRenderingFrame()->renderingCommandsLog.Add(L"RequestRendererEndBoundary");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void EnsureRenderedElement(vint id, Rect bounds)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::EnsureRenderedElement(id&)#"
|
|
|
|
vint index = loggedTrace.createdElements->Keys().IndexOf(id);
|
|
CHECK_ERROR(index != -1, ERROR_MESSAGE_PREFIX L"Renderer with the specified id has not been created.");
|
|
auto rendererType = loggedTrace.createdElements->Values()[index];
|
|
if (rendererType == remoteprotocol::RendererType::FocusRectangle)
|
|
{
|
|
// FocusRectangle does not has a ElementDesc
|
|
return;
|
|
}
|
|
|
|
index = lastElementDescs.Keys().IndexOf(id);
|
|
CHECK_ERROR(index != -1, ERROR_MESSAGE_PREFIX L"Renderer with the specified id has not been updated after created.");
|
|
lastElementDescs.Values()[index].Apply(Overloading(
|
|
[](remoteprotocol::RendererType)
|
|
{
|
|
CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Renderer with the specified id has not been updated after created.");
|
|
},
|
|
[&](const remoteprotocol::ElementDesc_SolidLabel& solidLabel)
|
|
{
|
|
CalculateSolidLabelSizeIfNecessary(bounds.Width(), bounds.Height(), solidLabel);
|
|
},
|
|
[&](const auto& element)
|
|
{
|
|
}));
|
|
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererRenderElement(const remoteprotocol::ElementRendering& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::RequestRendererRenderElement(const ElementRendering&)#"
|
|
CHECK_ERROR(!receivedDomDiffMessage, ERROR_MESSAGE_PREFIX L"This message could not be used with dom-diff messages in the same rendering cycle.");
|
|
if (!receivedElementMessage)
|
|
{
|
|
renderingDomBuilder.RequestRendererBeginRendering();
|
|
receivedElementMessage = true;
|
|
}
|
|
|
|
{
|
|
renderingDomBuilder.RequestRendererRenderElement(arguments);
|
|
|
|
glr::json::JsonFormatting formatting;
|
|
formatting.spaceAfterColon = true;
|
|
formatting.spaceAfterComma = true;
|
|
formatting.crlf = false;
|
|
formatting.compact = true;
|
|
GetLastRenderingFrame()->renderingCommandsLog.Add(L"RequestRendererRenderElement: " + stream::GenerateToStream([&](stream::TextWriter& writer)
|
|
{
|
|
auto jsonLog = remoteprotocol::ConvertCustomTypeToJson(arguments);
|
|
writer.WriteString(glr::json::JsonToString(jsonLog, formatting));
|
|
}));
|
|
}
|
|
|
|
EnsureRenderedElement(arguments.id, arguments.bounds);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
/***********************************************************************
|
|
IGuiRemoteProtocolMessages (Rendering - Dom)
|
|
***********************************************************************/
|
|
|
|
void CalculateSolidLabelSizesIfNecessary(Ptr<remoteprotocol::RenderingDom> dom)
|
|
{
|
|
if (dom->content.element)
|
|
{
|
|
EnsureRenderedElement(dom->content.element.Value(), dom->content.bounds);
|
|
}
|
|
if (dom->children)
|
|
{
|
|
for (auto child : *dom->children.Obj())
|
|
{
|
|
CalculateSolidLabelSizesIfNecessary(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RequestRendererRenderDom(const Ptr<remoteprotocol::RenderingDom>& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::RequestRendererRenderElement(const RenderingDom&)#"
|
|
CHECK_ERROR(!receivedElementMessage, ERROR_MESSAGE_PREFIX L"This message could not be used with element messages in the same rendering cycle.");
|
|
if (!receivedDomDiffMessage)
|
|
{
|
|
receivedDomDiffMessage = true;
|
|
}
|
|
|
|
receivedDom = arguments;
|
|
remoteprotocol::BuildDomIndex(receivedDom, receivedDomIndex);
|
|
CalculateSolidLabelSizesIfNecessary(receivedDom);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererRenderDomDiff(const remoteprotocol::RenderingDom_DiffsInOrder& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::RequestRendererRenderElement(const RenderingDom_DiffsInOrder&)#"
|
|
CHECK_ERROR(!receivedElementMessage, ERROR_MESSAGE_PREFIX L"This message could not be used with element messages in the same rendering cycle.");
|
|
if (!receivedDomDiffMessage)
|
|
{
|
|
receivedDomDiffMessage = true;
|
|
}
|
|
|
|
remoteprotocol::UpdateDomInplace(receivedDom, receivedDomIndex, arguments);
|
|
GetLastRenderingFrame()->renderingDiffs = arguments;
|
|
CalculateSolidLabelSizesIfNecessary(receivedDom);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
/***********************************************************************
|
|
IGuiRemoteProtocolMessages (Elements)
|
|
***********************************************************************/
|
|
|
|
void RequestRendererCreated(const Ptr<collections::List<remoteprotocol::RendererCreation>>& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::RequestRendererCreated(const Ptr<List<RendererCreation>>&)#"
|
|
if (arguments)
|
|
{
|
|
for (auto creation : *arguments.Obj())
|
|
{
|
|
CHECK_ERROR(!loggedTrace.createdElements->Keys().Contains(creation.id), ERROR_MESSAGE_PREFIX L"Renderer with the specified id has been created or used.");
|
|
loggedTrace.createdElements->Add(creation.id, creation.type);
|
|
}
|
|
}
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererDestroyed(const Ptr<collections::List<vint>>& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::RequestRendererDestroyed(const Ptr<List<vint>>&)#"
|
|
if (arguments)
|
|
{
|
|
for (auto id : *arguments.Obj())
|
|
{
|
|
CHECK_ERROR(loggedTrace.createdElements->Keys().Contains(id), ERROR_MESSAGE_PREFIX L"Renderer with the specified id has not been created.");
|
|
CHECK_ERROR(!removedElementIds.Contains(id), ERROR_MESSAGE_PREFIX L"Renderer with the specified id has been destroyed.");
|
|
removedElementIds.Add(id);
|
|
lastElementDescs.Remove(id);
|
|
}
|
|
}
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#define REQUEST_RENDERER_UPDATE_ELEMENT2(ARGUMENTS, RENDERER_TYPE)\
|
|
RequestRendererUpdateElement<RENDERER_TYPE>(\
|
|
ARGUMENTS,\
|
|
ERROR_MESSAGE_PREFIX L"Renderer with the specified id has not been created.",\
|
|
ERROR_MESSAGE_PREFIX L"Renderer with the specified id is not of the expected type."\
|
|
)
|
|
|
|
#define REQUEST_RENDERER_UPDATE_ELEMENT(RENDERER_TYPE) REQUEST_RENDERER_UPDATE_ELEMENT2(arguments, RENDERER_TYPE)
|
|
|
|
void RequestRendererUpdateElement_SolidBorder(const remoteprotocol::ElementDesc_SolidBorder& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::RequestRendererUpdateElement_SolidBorder<TProtocol>::RequestRendererCreated(const ElementDesc_SolidBorder&)#"
|
|
REQUEST_RENDERER_UPDATE_ELEMENT(remoteprotocol::RendererType::SolidBorder);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererUpdateElement_SinkBorder(const remoteprotocol::ElementDesc_SinkBorder& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::RequestRendererUpdateElement_SinkBorder<TProtocol>::RequestRendererCreated(const ElementDesc_SinkBorder&)#"
|
|
REQUEST_RENDERER_UPDATE_ELEMENT(remoteprotocol::RendererType::SinkBorder);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererUpdateElement_SinkSplitter(const remoteprotocol::ElementDesc_SinkSplitter& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::RequestRendererUpdateElement_SinkSplitter<TProtocol>::RequestRendererCreated(const ElementDesc_SinkSplitter&)#"
|
|
REQUEST_RENDERER_UPDATE_ELEMENT(remoteprotocol::RendererType::SinkSplitter);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererUpdateElement_SolidBackground(const remoteprotocol::ElementDesc_SolidBackground& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::RequestRendererUpdateElement_SolidBackground<TProtocol>::RequestRendererCreated(const ElementDesc_SolidBackground&)#"
|
|
REQUEST_RENDERER_UPDATE_ELEMENT(remoteprotocol::RendererType::SolidBackground);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererUpdateElement_GradientBackground(const remoteprotocol::ElementDesc_GradientBackground& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::RequestRendererUpdateElement_GradientBackground<TProtocol>::RequestRendererCreated(const ElementDesc_GradientBackground&)#"
|
|
REQUEST_RENDERER_UPDATE_ELEMENT(remoteprotocol::RendererType::GradientBackground);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererUpdateElement_InnerShadow(const remoteprotocol::ElementDesc_InnerShadow& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::RequestRendererUpdateElement_InnerShadow<TProtocol>::RequestRendererCreated(const ElementDesc_InnerShadow&)#"
|
|
REQUEST_RENDERER_UPDATE_ELEMENT(remoteprotocol::RendererType::InnerShadow);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererUpdateElement_Polygon(const remoteprotocol::ElementDesc_Polygon& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::RequestRendererUpdateElement_Polygon<TProtocol>::RequestRendererCreated(const ElementDesc_Polygon&)#"
|
|
REQUEST_RENDERER_UPDATE_ELEMENT(remoteprotocol::RendererType::Polygon);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
/***********************************************************************
|
|
IGuiRemoteProtocolMessages (Elements - SolidLabel)
|
|
***********************************************************************/
|
|
|
|
void CalculateSolidLabelSizeIfNecessary(vint width, vint height, const remoteprotocol::ElementDesc_SolidLabel& arguments)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::RequestRendererUpdateElement_SolidLabel<TProtocol>::CalculateSolidLabelSizeIfNecessary(vint, vint, const ElementDesc_SolidLabel&)#"
|
|
|
|
if (arguments.measuringRequest)
|
|
{
|
|
switch (arguments.measuringRequest.Value())
|
|
{
|
|
case remoteprotocol::ElementSolidLabelMeasuringRequest::FontHeight:
|
|
CHECK_ERROR(arguments.font, ERROR_MESSAGE_PREFIX L"Font is missing for calculating font height.");
|
|
if (!measuringForNextRendering.fontHeights)
|
|
{
|
|
measuringForNextRendering.fontHeights = Ptr(new collections::List<remoteprotocol::ElementMeasuring_FontHeight>);
|
|
}
|
|
{
|
|
remoteprotocol::ElementMeasuring_FontHeight measuring;
|
|
measuring.fontFamily = arguments.font.Value().fontFamily;
|
|
measuring.fontSize = arguments.font.Value().size;
|
|
measuring.height = measuring.fontSize + 4;
|
|
measuringForNextRendering.fontHeights->Add(measuring);
|
|
}
|
|
break;
|
|
case remoteprotocol::ElementSolidLabelMeasuringRequest::TotalSize:
|
|
{
|
|
// font and text has already been verified exist in RequestRendererUpdateElement_SolidLabel
|
|
vint size = arguments.font.Value().size;
|
|
auto text = arguments.text.Value();
|
|
vint textWidth = 0;
|
|
vint textHeight = 0;
|
|
|
|
collections::List<vint> lines;
|
|
{
|
|
collections::List<Ptr<regex::RegexMatch>> matches;
|
|
regexCrLf.Split(text, true, matches);
|
|
|
|
if (matches.Count() == 0)
|
|
{
|
|
// when there is no text, measure a space
|
|
lines.Add(1);
|
|
}
|
|
else
|
|
{
|
|
auto normalizedLines =
|
|
From(matches)
|
|
.Select([](auto&& match) { return match->Result().Length(); })
|
|
.Select([](vint length) { return length ? length : 1; })
|
|
;
|
|
if (arguments.multiline)
|
|
{
|
|
// calculate text as multiple lines
|
|
CopyFrom(
|
|
lines,
|
|
normalizedLines
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// calculate text as single line, insert a space between each line
|
|
lines.Add(
|
|
normalizedLines
|
|
.template Aggregate<vint>(-1, [](auto a, auto b) { return a + b + 1; })
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (arguments.wrapLine)
|
|
{
|
|
// width of the text is 0
|
|
// insert a line break when there is no space horizontally
|
|
textHeight = 4 + size * From(lines)
|
|
.Select([columns = width / size](vint length)
|
|
{
|
|
if (columns == 0)
|
|
{
|
|
return length;
|
|
}
|
|
else
|
|
{
|
|
return (length + columns - 1) / columns;
|
|
}
|
|
})
|
|
.template Aggregate<vint>(0, [](auto a, auto b) { return a + b; });
|
|
}
|
|
else
|
|
{
|
|
// width of the text is width of the longest line
|
|
textWidth = size * From(lines).Max();
|
|
textHeight = 4 + size * lines.Count();
|
|
}
|
|
|
|
if (!measuringForNextRendering.minSizes)
|
|
{
|
|
measuringForNextRendering.minSizes = Ptr(new collections::List<remoteprotocol::ElementMeasuring_ElementMinSize>);
|
|
}
|
|
{
|
|
remoteprotocol::ElementMeasuring_ElementMinSize measuring;
|
|
measuring.id = arguments.id;
|
|
measuring.minSize = { textWidth,textHeight };
|
|
measuringForNextRendering.minSizes->Add(measuring);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
CHECK_FAIL(L"Unknown value of ElementSolidLabelMeasuringRequest.");
|
|
}
|
|
}
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererUpdateElement_SolidLabel(const remoteprotocol::ElementDesc_SolidLabel& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::RequestRendererUpdateElement_SolidLabel<TProtocol>::RequestRendererCreated(const ElementDesc_SolidLabel&)#"
|
|
auto element = arguments;
|
|
if (!element.font || !element.text)
|
|
{
|
|
vint index = loggedTrace.createdElements->Keys().IndexOf(element.id);
|
|
CHECK_ERROR(index != -1, ERROR_MESSAGE_PREFIX L"Renderer with the specified id has not been created.");
|
|
|
|
auto rendererType = loggedTrace.createdElements->Values()[index];
|
|
CHECK_ERROR(rendererType == remoteprotocol::RendererType::SolidLabel, ERROR_MESSAGE_PREFIX L"Renderer with the specified id is not of the expected type.");
|
|
|
|
index = lastElementDescs.Keys().IndexOf(arguments.id);
|
|
if (index != -1)
|
|
{
|
|
auto solidLabel = lastElementDescs.Values()[index].TryGet<remoteprotocol::ElementDesc_SolidLabel>();
|
|
CHECK_ERROR(solidLabel, ERROR_MESSAGE_PREFIX L"Renderer with the specified id is not of the expected type.");
|
|
if (!element.font) element.font = solidLabel->font;
|
|
if (!element.text) element.text = solidLabel->text;
|
|
}
|
|
else
|
|
{
|
|
if (!element.font) element.font = FontProperties();
|
|
if (!element.text) element.text = WString::Empty;
|
|
}
|
|
}
|
|
REQUEST_RENDERER_UPDATE_ELEMENT2(element, remoteprotocol::RendererType::SolidLabel);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
/***********************************************************************
|
|
IGuiRemoteProtocolMessages (Elements - Image)
|
|
***********************************************************************/
|
|
|
|
WString GetBinaryKeyFromBinary(stream::IStream& binary)
|
|
{
|
|
stream::MemoryStream base64WStringStream;
|
|
{
|
|
stream::UtfGeneralEncoder<wchar_t, char8_t> utf8ToWCharEncoder;
|
|
stream::EncoderStream utf8ToWCharStream(base64WStringStream, utf8ToWCharEncoder);
|
|
stream::Utf8Base64Encoder binaryToBase64Utf8Encoder;
|
|
stream::EncoderStream binaryToBase64Utf8Stream(utf8ToWCharStream, binaryToBase64Utf8Encoder);
|
|
binary.SeekFromBegin(0);
|
|
stream::CopyStream(binary, binaryToBase64Utf8Stream);
|
|
}
|
|
{
|
|
base64WStringStream.SeekFromBegin(0);
|
|
stream::StreamReader reader(base64WStringStream);
|
|
return reader.ReadToEnd();
|
|
}
|
|
}
|
|
|
|
WString GetBinaryKeyFromImage(Ptr<INativeImage> image)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::GetBinaryKeyFromImage(Ptr<INativeImage>)#"
|
|
auto remoteImage = image.Cast<GuiRemoteGraphicsImage>();
|
|
CHECK_ERROR(remoteImage, ERROR_MESSAGE_PREFIX L"The image object must be GuiRemoteGraphicsImage.");
|
|
return GetBinaryKeyFromBinary(remoteImage->GetBinaryData());
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
remoteprotocol::ImageMetadata MakeImageMetadata(const remoteprotocol::ImageCreation& arguments)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::MakeImageMetadata(const remoteprotocol::ImageCreation)#"
|
|
if (!cachedImageMetadatas)
|
|
{
|
|
cachedImageMetadatas = Ptr(new Base64ToImageMetadataMap);
|
|
for (auto resource : GetResourceManager()->GetLoadedResources())
|
|
{
|
|
if (auto xmlImageData = resource->GetValueByPath(WString::Unmanaged(L"UnitTestConfig/ImageData")).Cast<glr::xml::XmlDocument>())
|
|
{
|
|
for (auto elementImage : glr::xml::XmlGetElements(xmlImageData->rootElement, WString::Unmanaged(L"Image")))
|
|
{
|
|
WString path, format, frames = WString::Unmanaged(L"1"), width, height;
|
|
|
|
auto attPath = glr::xml::XmlGetAttribute(elementImage.Obj(), WString::Unmanaged(L"Path"));
|
|
auto attFormat = glr::xml::XmlGetAttribute(elementImage.Obj(), WString::Unmanaged(L"Format"));
|
|
auto attFrames = glr::xml::XmlGetAttribute(elementImage.Obj(), WString::Unmanaged(L"Frames"));
|
|
auto attWidth = glr::xml::XmlGetAttribute(elementImage.Obj(), WString::Unmanaged(L"Width"));
|
|
auto attHeight = glr::xml::XmlGetAttribute(elementImage.Obj(), WString::Unmanaged(L"Height"));
|
|
|
|
CHECK_ERROR(attPath, ERROR_MESSAGE_PREFIX L"Missing Path attribute in Image element in an UnitTestConfig/ImageData.");
|
|
CHECK_ERROR(attFormat, ERROR_MESSAGE_PREFIX L"Missing Format attribute in Image element in an UnitTestConfig/ImageData.");
|
|
CHECK_ERROR(attWidth, ERROR_MESSAGE_PREFIX L"Missing Width attribute in Image element in an UnitTestConfig/ImageData.");
|
|
CHECK_ERROR(attHeight, ERROR_MESSAGE_PREFIX L"Missing Height attribute in Image element in an UnitTestConfig/ImageData.");
|
|
|
|
path = attPath->value.value;
|
|
format = attFormat->value.value;
|
|
width = attWidth->value.value;
|
|
height = attHeight->value.value;
|
|
if (attFrames) frames = attFrames->value.value;
|
|
|
|
vint valueFrames = wtoi(frames);
|
|
vint valueWidth = wtoi(width);
|
|
vint valueHeight = wtoi(height);
|
|
|
|
CHECK_ERROR(itow(valueFrames) == frames, ERROR_MESSAGE_PREFIX L"Frames attribute must be an integer in Image element in an UnitTestConfig/ImageData.");
|
|
CHECK_ERROR(itow(valueWidth) == width, ERROR_MESSAGE_PREFIX L"Width attribute must be an integer in Image element in an UnitTestConfig/ImageData.");
|
|
CHECK_ERROR(itow(valueHeight) == height, ERROR_MESSAGE_PREFIX L"Height attribute must be an integer in Image element in an UnitTestConfig/ImageData.");
|
|
|
|
auto imageData = resource->GetImageByPath(path);
|
|
WString binaryKey = GetBinaryKeyFromImage(imageData->GetImage());
|
|
|
|
if (!cachedImageMetadatas->Keys().Contains(binaryKey))
|
|
{
|
|
remoteprotocol::ImageMetadata imageMetadata;
|
|
imageMetadata.id = -1;
|
|
imageMetadata.frames = Ptr(new collections::List<remoteprotocol::ImageFrameMetadata>);
|
|
{
|
|
auto node = Ptr(new glr::json::JsonString);
|
|
node->content.value = format;
|
|
remoteprotocol::ConvertJsonToCustomType(node, imageMetadata.format);
|
|
}
|
|
for (vint frame = 0; frame < valueFrames; frame++)
|
|
{
|
|
imageMetadata.frames->Add({ {valueWidth,valueHeight} });
|
|
}
|
|
|
|
cachedImageMetadatas->Add(binaryKey, imageMetadata);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
auto binaryKey = GetBinaryKeyFromBinary(*arguments.imageData.Obj());
|
|
vint binaryIndex = cachedImageMetadatas->Keys().IndexOf(binaryKey);
|
|
CHECK_ERROR(binaryIndex != -1, ERROR_MESSAGE_PREFIX L"The image is not registered in any UnitTestConfig/ImageData.");
|
|
auto metadata = cachedImageMetadatas->Values()[binaryIndex];
|
|
metadata.id = arguments.id;
|
|
|
|
loggedTrace.imageCreations->Add(arguments);
|
|
loggedTrace.imageMetadatas->Add(metadata);
|
|
return metadata;
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestImageCreated(vint id, const remoteprotocol::ImageCreation& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::RequestImageCreated(vint, const vint&)#"
|
|
CHECK_ERROR(!loggedTrace.imageMetadatas->Keys().Contains(arguments.id), ERROR_MESSAGE_PREFIX L"Image with the specified id has been created or used.");
|
|
this->GetEvents()->RespondImageCreated(id, MakeImageMetadata(arguments));
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestImageDestroyed(const vint& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering<TProtocol>::RequestImageDestroyed(const vint&)#"
|
|
CHECK_ERROR(loggedTrace.imageMetadatas->Keys().Contains(arguments), ERROR_MESSAGE_PREFIX L"Image with the specified id has not been created.");
|
|
CHECK_ERROR(!removedImageIds.Contains(arguments), ERROR_MESSAGE_PREFIX L"Image with the specified id has been destroyed.");
|
|
removedImageIds.Add(arguments);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RequestRendererUpdateElement_ImageFrame(const remoteprotocol::ElementDesc_ImageFrame& arguments) override
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::RequestRendererUpdateElement_ImageFrame<TProtocol>::RequestRendererCreated(const ElementDesc_ImageFrame&)#"
|
|
if (arguments.imageCreation)
|
|
{
|
|
auto&& imageCreation = arguments.imageCreation.Value();
|
|
if (!imageCreation.imageDataOmitted)
|
|
{
|
|
CHECK_ERROR(arguments.imageId && arguments.imageId.Value() != !imageCreation.id, ERROR_MESSAGE_PREFIX L"It should satisfy that (arguments.imageId.Value()id == imageCreation.id).");
|
|
CHECK_ERROR(!loggedTrace.imageMetadatas->Keys().Contains(imageCreation.id), ERROR_MESSAGE_PREFIX L"Image with the specified id has been created.");
|
|
CHECK_ERROR(imageCreation.imageData, ERROR_MESSAGE_PREFIX L"When imageDataOmitted == false, imageData should not be null.");
|
|
if (!measuringForNextRendering.createdImages)
|
|
{
|
|
measuringForNextRendering.createdImages = Ptr(new collections::List<remoteprotocol::ImageMetadata>);
|
|
}
|
|
measuringForNextRendering.createdImages->Add(MakeImageMetadata(imageCreation));
|
|
}
|
|
else
|
|
{
|
|
CHECK_ERROR(!imageCreation.imageData, ERROR_MESSAGE_PREFIX L"When imageDataOmitted == true, imageData should be null.");
|
|
}
|
|
}
|
|
else if (arguments.imageId)
|
|
{
|
|
CHECK_ERROR(loggedTrace.imageMetadatas->Keys().Contains(arguments.imageId.Value()), ERROR_MESSAGE_PREFIX L"Image with the specified id has not been created.");
|
|
}
|
|
|
|
auto element = arguments;
|
|
element.imageCreation.Reset();
|
|
REQUEST_RENDERER_UPDATE_ELEMENT2(element, remoteprotocol::RendererType::ImageFrame);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
#undef REQUEST_RENDERER_UPDATE_ELEMENT
|
|
#undef REQUEST_RENDERER_UPDATE_ELEMENT2
|
|
};
|
|
}
|
|
|
|
#endif
|
|
|
|
/***********************************************************************
|
|
.\GUIUNITTESTPROTOCOL_LOGGING.H
|
|
***********************************************************************/
|
|
/***********************************************************************
|
|
Vczh Library++ 3.0
|
|
Developer: Zihan Chen(vczh)
|
|
Unit Test Snapsnot and other Utilities
|
|
***********************************************************************/
|
|
|
|
#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_LOGGING
|
|
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_LOGGING
|
|
|
|
|
|
namespace vl::presentation::unittest
|
|
{
|
|
|
|
/***********************************************************************
|
|
UnitTestRemoteProtocol
|
|
***********************************************************************/
|
|
|
|
template<typename TProtocol>
|
|
class UnitTestRemoteProtocol_Logging : public TProtocol
|
|
{
|
|
protected:
|
|
bool everRendered = false;
|
|
Ptr<UnitTestLoggedFrame> candidateFrame;
|
|
|
|
bool LogRenderingResult()
|
|
{
|
|
if (auto lastFrame = this->TryGetLastRenderingFrameAndReset())
|
|
{
|
|
candidateFrame = lastFrame;
|
|
everRendered = true;
|
|
}
|
|
else if (everRendered)
|
|
{
|
|
if (candidateFrame)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
|
|
template<typename ...TArgs>
|
|
UnitTestRemoteProtocol_Logging(TArgs&& ...args)
|
|
: TProtocol(std::forward<TArgs&&>(args)...)
|
|
{
|
|
}
|
|
|
|
const auto& GetLoggedTrace()
|
|
{
|
|
return this->loggedTrace;
|
|
}
|
|
|
|
const auto& GetLoggedFrames()
|
|
{
|
|
return this->loggedFrames;
|
|
}
|
|
};
|
|
}
|
|
|
|
#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
|
|
***********************************************************************/
|
|
|
|
template<typename TBase, template<typename> class ...TMixins>
|
|
struct Mixin;
|
|
|
|
template<typename TBase>
|
|
struct Mixin<TBase>
|
|
{
|
|
using Type = TBase;
|
|
};
|
|
|
|
template<typename TBase, template<typename> class TMixin, template<typename> class ...TOtherMixins>
|
|
struct Mixin<TBase, TMixin, TOtherMixins...>
|
|
{
|
|
using Type = typename Mixin<TMixin<TBase>, TOtherMixins...>::Type;
|
|
};
|
|
|
|
using UnitTestRemoteProtocolFeatures = Mixin<
|
|
UnitTestRemoteProtocolBase,
|
|
UnitTestRemoteProtocol_MainWindow,
|
|
UnitTestRemoteProtocol_IO,
|
|
UnitTestRemoteProtocol_Rendering,
|
|
UnitTestRemoteProtocol_Logging,
|
|
UnitTestRemoteProtocol_IOCommands
|
|
>::Type;
|
|
|
|
class UnitTestRemoteProtocol : public UnitTestRemoteProtocolFeatures
|
|
{
|
|
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 stopped = false;
|
|
|
|
public:
|
|
|
|
UnitTestRemoteProtocol(const WString& _appName, UnitTestScreenConfig _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) });
|
|
}
|
|
|
|
protected:
|
|
|
|
/***********************************************************************
|
|
IGuiRemoteProtocolMessages (Initialization)
|
|
***********************************************************************/
|
|
|
|
void RequestControllerConnectionEstablished() override
|
|
{
|
|
ResetCreatedObjects();
|
|
}
|
|
|
|
void RequestControllerConnectionStopped() override
|
|
{
|
|
stopped = true;
|
|
}
|
|
|
|
/***********************************************************************
|
|
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];
|
|
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;
|
|
}
|
|
func();
|
|
nextEventIndex++;
|
|
}
|
|
}
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
};
|
|
}
|
|
|
|
#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
|
|
|
|
template<typename TTheme>
|
|
void GacUIUnitTest_StartFast_WithResourceAsText(
|
|
const vl::WString& appName,
|
|
const vl::WString& windowTypeFullName,
|
|
const vl::WString& resourceText, vl::Func<void(vl::presentation::controls::GuiWindow*)> installWindow,
|
|
vl::Nullable<vl::presentation::unittest::UnitTestScreenConfig> config
|
|
)
|
|
{
|
|
GacUIUnitTest_LinkGuiMainProxy([=](
|
|
vl::presentation::unittest::UnitTestRemoteProtocol* protocol,
|
|
vl::presentation::unittest::IUnitTestContext* context,
|
|
const vl::presentation::unittest::UnitTestMainFunc& previousMainProxy
|
|
)
|
|
{
|
|
protocol->GetEvents()->OnControllerConnect();
|
|
auto theme = vl::Ptr(new TTheme);
|
|
vl::presentation::theme::RegisterTheme(theme);
|
|
{
|
|
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 (installWindow)
|
|
{
|
|
installWindow(window.Obj());
|
|
}
|
|
window->MoveToScreenCenter();
|
|
previousMainProxy(protocol, context);
|
|
vl::presentation::controls::GetApplication()->Run(window.Obj());
|
|
}
|
|
vl::presentation::theme::UnregisterTheme(theme->Name);
|
|
});
|
|
GacUIUnitTest_Start_WithResourceAsText(appName, config, resourceText);
|
|
}
|
|
|
|
template<typename TTheme>
|
|
void GacUIUnitTest_StartFast_WithResourceAsText(
|
|
const vl::WString& appName,
|
|
const vl::WString& windowTypeFullName,
|
|
const vl::WString& resourceText,
|
|
vl::Nullable<vl::presentation::unittest::UnitTestScreenConfig> config
|
|
)
|
|
{
|
|
GacUIUnitTest_StartFast_WithResourceAsText<TTheme>(appName, windowTypeFullName, resourceText, {}, config);
|
|
}
|
|
|
|
template<typename TTheme>
|
|
void GacUIUnitTest_StartFast_WithResourceAsText(
|
|
const vl::WString& appName,
|
|
const vl::WString& windowTypeFullName,
|
|
const vl::WString& resourceText,
|
|
vl::Func<void(vl::presentation::controls::GuiWindow*)> installWindow
|
|
)
|
|
{
|
|
GacUIUnitTest_StartFast_WithResourceAsText<TTheme>(appName, windowTypeFullName, resourceText, installWindow, {});
|
|
}
|
|
|
|
template<typename TTheme>
|
|
void GacUIUnitTest_StartFast_WithResourceAsText(
|
|
const vl::WString& appName,
|
|
const vl::WString& windowTypeFullName,
|
|
const vl::WString& resourceText
|
|
)
|
|
{
|
|
GacUIUnitTest_StartFast_WithResourceAsText<TTheme>(appName, windowTypeFullName, resourceText, {}, {});
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|