chore: update to 2a0400ade5535001bfc09e0e7531515e232be6e7
This commit is contained in:
57
libs/imgui_test_engine/LICENSE.txt
Normal file
57
libs/imgui_test_engine/LICENSE.txt
Normal file
@@ -0,0 +1,57 @@
|
||||
Dear ImGui Test Engine License (v1.03)
|
||||
Copyright (c) 2018-2023 Omar Cornut
|
||||
|
||||
This document is a legal agreement ("License") between you ("Licensee") and
|
||||
DISCO HELLO ("Licensor") that governs your use of Dear ImGui Test Engine ("Software").
|
||||
|
||||
1. LICENSE MODELS
|
||||
|
||||
1.1. Free license
|
||||
|
||||
The Licensor grants you a free license ("Free License") if you meet ANY of the following
|
||||
criterion:
|
||||
|
||||
- You are a natural person;
|
||||
- You are not a legal entity, or you are a not-for-profit legal entity;
|
||||
- You are using this Software for educational purposes;
|
||||
- You are using this Software to create Derivative Software released publicly and under
|
||||
an Open Source license, as defined by the Open Source Initiative;
|
||||
- You are a legal entity with a turnover inferior to 2 million USD (or equivalent) during
|
||||
your last fiscal year.
|
||||
|
||||
1.2. Paid license
|
||||
|
||||
If you do not meet any criterion of Article 1.1, Licensor grants you a trial period of a
|
||||
maximum of 45 days, at no charge. Following this trial period, you must subscribe to a paid
|
||||
license ("Paid License") with the Licensor to continue using the Software.
|
||||
Paid Licenses are exclusively sold by DISCO HELLO. Paid Licenses and the associated
|
||||
information are available at the following URL: http://www.dearimgui.com/licenses
|
||||
|
||||
2. GRANT OF LICENSE
|
||||
|
||||
2.1. License scope
|
||||
|
||||
A limited and non-exclusive license is hereby granted, to the Licensee, to reproduce,
|
||||
execute, publicly perform, and display, use, copy, modify, merge, distribute, or create
|
||||
derivative works based on and/or derived from the Software ("Derivative Software").
|
||||
|
||||
2.2. Right of distribution
|
||||
|
||||
License holders may also publish and/or distribute the Software or any Derivative
|
||||
Software. The above copyright notice and this license shall be included in all copies
|
||||
or substantial portions of the Software and/or Derivative Software.
|
||||
|
||||
License holders may add their own attribution notices within the Derivative Software
|
||||
that they distribute. Such attribution notices must not directly or indirectly imply a
|
||||
modification of the License. License holders may provide to their modifications their
|
||||
own copyright and/or additional or different terms and conditions, providing such
|
||||
conditions complies with this License.
|
||||
|
||||
3. DISCLAIMER
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
1114
libs/imgui_test_engine/imgui_capture_tool.cpp
Normal file
1114
libs/imgui_test_engine/imgui_capture_tool.cpp
Normal file
File diff suppressed because it is too large
Load Diff
180
libs/imgui_test_engine/imgui_capture_tool.h
Normal file
180
libs/imgui_test_engine/imgui_capture_tool.h
Normal file
@@ -0,0 +1,180 @@
|
||||
// dear imgui test engine
|
||||
// (screen/video capture tool)
|
||||
// This is usable as a standalone applet or controlled by the test engine.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui_te_utils.h" // ImFuncPtr
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Our types
|
||||
struct ImGuiCaptureArgs; // Parameters for Capture
|
||||
struct ImGuiCaptureContext; // State of an active capture tool
|
||||
struct ImGuiCaptureImageBuf; // Simple helper to store an RGBA image in memory
|
||||
struct ImGuiCaptureToolUI; // Capture tool instance + UI window
|
||||
|
||||
typedef unsigned int ImGuiCaptureFlags; // See enum: ImGuiCaptureFlags_
|
||||
|
||||
// Capture function which needs to be provided by user application
|
||||
typedef bool (ImGuiScreenCaptureFunc)(ImGuiID viewport_id, int x, int y, int w, int h, unsigned int* pixels, void* user_data);
|
||||
|
||||
// External types
|
||||
struct ImGuiWindow; // imgui.h
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// [Internal]
|
||||
// Helper class for simple bitmap manipulation (not particularly efficient!)
|
||||
struct IMGUI_API ImGuiCaptureImageBuf
|
||||
{
|
||||
int Width;
|
||||
int Height;
|
||||
unsigned int* Data; // RGBA8
|
||||
|
||||
ImGuiCaptureImageBuf() { Width = Height = 0; Data = NULL; }
|
||||
~ImGuiCaptureImageBuf() { Clear(); }
|
||||
|
||||
void Clear(); // Free allocated memory buffer if such exists.
|
||||
void CreateEmpty(int w, int h); // Reallocate buffer for pixel data and zero it.
|
||||
bool SaveFile(const char* filename); // Save pixel data to specified image file.
|
||||
void RemoveAlpha(); // Clear alpha channel from all pixels.
|
||||
};
|
||||
|
||||
enum ImGuiCaptureFlags_ : unsigned int
|
||||
{
|
||||
ImGuiCaptureFlags_None = 0,
|
||||
ImGuiCaptureFlags_StitchAll = 1 << 0, // Capture entire window scroll area (by scrolling and taking multiple screenshot). Only works for a single window.
|
||||
ImGuiCaptureFlags_IncludeOtherWindows = 1 << 1, // Disable hiding other windows (when CaptureAddWindow has been called by default other windows are hidden)
|
||||
ImGuiCaptureFlags_IncludeTooltipsAndPopups = 1 << 2, // Expand capture area to automatically include visible popups and tooltips (use with ImGuiCaptureflags_HideOtherWindows)
|
||||
ImGuiCaptureFlags_HideMouseCursor = 1 << 3, // Hide render software mouse cursor during capture.
|
||||
ImGuiCaptureFlags_Instant = 1 << 4, // Perform capture on very same frame. Only works when capturing a rectangular region. Unsupported features: content stitching, window hiding, window relocation.
|
||||
ImGuiCaptureFlags_NoSave = 1 << 5 // Do not save output image.
|
||||
};
|
||||
|
||||
// Defines input and output arguments for capture process.
|
||||
// When capturing from tests you can usually use the ImGuiTestContext::CaptureXXX() helpers functions.
|
||||
struct ImGuiCaptureArgs
|
||||
{
|
||||
// [Input]
|
||||
ImGuiCaptureFlags InFlags = 0; // Flags for customizing behavior of screenshot tool.
|
||||
ImVector<ImGuiWindow*> InCaptureWindows; // Windows to capture. All other windows will be hidden. May be used with InCaptureRect to capture only some windows in specified rect.
|
||||
ImRect InCaptureRect; // Screen rect to capture. Does not include padding.
|
||||
float InPadding = 16.0f; // Extra padding at the edges of the screenshot. Ensure that there is available space around capture rect horizontally, also vertically if ImGuiCaptureFlags_StitchFullContents is not used.
|
||||
char InOutputFile[256] = ""; // Output will be saved to a file if InOutputImageBuf is NULL.
|
||||
ImGuiCaptureImageBuf* InOutputImageBuf = NULL; // _OR_ Output will be saved to image buffer if specified.
|
||||
int InRecordFPSTarget = 30; // FPS target for recording videos.
|
||||
int InSizeAlign = 0; // Resolution alignment (0 = auto, 1 = no alignment, >= 2 = align width/height to be multiple of given value)
|
||||
|
||||
// [Output]
|
||||
ImVec2 OutImageSize; // Produced image size.
|
||||
};
|
||||
|
||||
enum ImGuiCaptureStatus
|
||||
{
|
||||
ImGuiCaptureStatus_InProgress,
|
||||
ImGuiCaptureStatus_Done,
|
||||
ImGuiCaptureStatus_Error
|
||||
};
|
||||
|
||||
struct ImGuiCaptureWindowData
|
||||
{
|
||||
ImGuiWindow* Window;
|
||||
ImRect BackupRect;
|
||||
ImVec2 PosDuringCapture;
|
||||
};
|
||||
|
||||
// Implements functionality for capturing images
|
||||
struct IMGUI_API ImGuiCaptureContext
|
||||
{
|
||||
// IO
|
||||
ImFuncPtr(ImGuiScreenCaptureFunc) ScreenCaptureFunc = NULL; // Graphics backend specific function that captures specified portion of framebuffer and writes RGBA data to `pixels` buffer.
|
||||
void* ScreenCaptureUserData = NULL; // Custom user pointer which is passed to ScreenCaptureFunc. (Optional)
|
||||
char* VideoCaptureEncoderPath = NULL; // Video encoder path (not owned, stored externally).
|
||||
int VideoCaptureEncoderPathSize = 0; // Optional. Set in order to edit this parameter from UI.
|
||||
char* VideoCaptureEncoderParams = NULL; // Video encoder params (not owned, stored externally).
|
||||
int VideoCaptureEncoderParamsSize = 0; // Optional. Set in order to edit this parameter from UI.
|
||||
char* GifCaptureEncoderParams = NULL; // Video encoder params for GIF output (not owned, stored externally).
|
||||
int GifCaptureEncoderParamsSize = 0; // Optional. Set in order to edit this parameter from UI.
|
||||
|
||||
// [Internal]
|
||||
ImRect _CaptureRect; // Viewport rect that is being captured.
|
||||
ImRect _CapturedWindowRect; // Top-left corner of region that covers all windows included in capture. This is not same as _CaptureRect.Min when capturing explicitly specified rect.
|
||||
int _ChunkNo = 0; // Number of chunk that is being captured when capture spans multiple frames.
|
||||
int _FrameNo = 0; // Frame number during capture process that spans multiple frames.
|
||||
ImVec2 _MouseRelativeToWindowPos; // Mouse cursor position relative to captured window (when _StitchAll is in use).
|
||||
ImGuiWindow* _HoveredWindow = NULL; // Window which was hovered at capture start.
|
||||
ImGuiCaptureImageBuf _CaptureBuf; // Output image buffer.
|
||||
const ImGuiCaptureArgs* _CaptureArgs = NULL; // Current capture args. Set only if capture is in progress.
|
||||
ImVector<ImGuiCaptureWindowData> _WindowsData; // Backup windows that will have their rect modified and restored. args->InCaptureWindows can not be used because popups may get closed during capture and no longer appear in that list.
|
||||
|
||||
// [Internal] Video recording
|
||||
bool _VideoRecording = false; // Flag indicating that video recording is in progress.
|
||||
double _VideoLastFrameTime = 0; // Time when last video frame was recorded.
|
||||
FILE* _VideoEncoderPipe = NULL; // File writing to stdin of video encoder process.
|
||||
|
||||
// [Internal] Backups
|
||||
bool _BackupMouseDrawCursor = false; // Initial value of g.IO.MouseDrawCursor
|
||||
ImVec2 _BackupDisplayWindowPadding; // Backup padding. We set it to {0, 0} during capture.
|
||||
ImVec2 _BackupDisplaySafeAreaPadding; // Backup padding. We set it to {0, 0} during capture.
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Functions
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
ImGuiCaptureContext(ImGuiScreenCaptureFunc capture_func = NULL) { ScreenCaptureFunc = capture_func; _MouseRelativeToWindowPos = ImVec2(-FLT_MAX, -FLT_MAX); }
|
||||
|
||||
// These functions should be called from appropriate context hooks. See ImGui::AddContextHook() for more info.
|
||||
// (ImGuiTestEngine automatically calls that for you, so this only apply to independently created instance)
|
||||
void PreNewFrame();
|
||||
void PreRender();
|
||||
void PostRender();
|
||||
|
||||
// Update capturing. If this function returns true then it should be called again with same arguments on the next frame.
|
||||
ImGuiCaptureStatus CaptureUpdate(ImGuiCaptureArgs* args);
|
||||
void RestoreBackedUpData();
|
||||
void ClearState();
|
||||
|
||||
// Begin video capture. Call CaptureUpdate() every frame afterwards until it returns false.
|
||||
void BeginVideoCapture(ImGuiCaptureArgs* args);
|
||||
void EndVideoCapture();
|
||||
bool IsCapturingVideo();
|
||||
bool IsCapturing();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ImGuiCaptureToolUI
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Implements UI for capturing images
|
||||
// (when using ImGuiTestEngine scripting API you may not need to use this at all)
|
||||
struct IMGUI_API ImGuiCaptureToolUI
|
||||
{
|
||||
float SnapGridSize = 32.0f; // Size of the grid cell for "snap to grid" functionality.
|
||||
char OutputLastFilename[256] = ""; // File name of last captured file.
|
||||
char* VideoCaptureExtension = NULL; // Video file extension (e.g. ".gif" or ".mp4")
|
||||
int VideoCaptureExtensionSize = 0; // Optional. Set in order to edit this parameter from UI.
|
||||
|
||||
ImGuiCaptureArgs _CaptureArgs; // Capture args
|
||||
bool _StateIsPickingWindow = false;
|
||||
bool _StateIsCapturing = false;
|
||||
ImVector<ImGuiID> _SelectedWindows;
|
||||
char _OutputFileTemplate[256] = ""; //
|
||||
int _FileCounter = 0; // Counter which may be appended to file name when saving. By default, counting starts from 1. When done this field holds number of saved files.
|
||||
|
||||
// Public
|
||||
ImGuiCaptureToolUI();
|
||||
void ShowCaptureToolWindow(ImGuiCaptureContext* context, bool* p_open = NULL); // Render a capture tool window with various options and utilities.
|
||||
|
||||
// [Internal]
|
||||
void _CaptureWindowPicker(ImGuiCaptureArgs* args); // Render a window picker that captures picked window to file specified in file_name.
|
||||
void _CaptureWindowsSelector(ImGuiCaptureContext* context, ImGuiCaptureArgs* args); // Render a selector for selecting multiple windows for capture.
|
||||
void _SnapWindowsToGrid(float cell_size); // Snap edges of all visible windows to a virtual grid.
|
||||
bool _InitializeOutputFile(); // Format output file template into capture args struct and ensure target directory exists.
|
||||
bool _ShowEncoderConfigFields(ImGuiCaptureContext* context);
|
||||
};
|
||||
|
||||
#define IMGUI_CAPTURE_DEFAULT_VIDEO_PARAMS_FOR_FFMPEG "-hide_banner -loglevel error -r $FPS -f rawvideo -pix_fmt rgba -s $WIDTHx$HEIGHT -i - -threads 0 -y -preset ultrafast -pix_fmt yuv420p -crf 20 $OUTPUT"
|
||||
#define IMGUI_CAPTURE_DEFAULT_GIF_PARAMS_FOR_FFMPEG "-hide_banner -loglevel error -r $FPS -f rawvideo -pix_fmt rgba -s $WIDTHx$HEIGHT -i - -threads 0 -y -filter_complex \"split=2 [a] [b]; [a] palettegen [pal]; [b] [pal] paletteuse\" $OUTPUT"
|
||||
3960
libs/imgui_test_engine/imgui_te_context.cpp
Normal file
3960
libs/imgui_test_engine/imgui_te_context.cpp
Normal file
File diff suppressed because it is too large
Load Diff
616
libs/imgui_test_engine/imgui_te_context.h
Normal file
616
libs/imgui_test_engine/imgui_te_context.h
Normal file
@@ -0,0 +1,616 @@
|
||||
// dear imgui test engine
|
||||
// (context when a running test + end user automation API)
|
||||
// This is the main (if not only) interface that your Tests will be using.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h" // ImGuiAxis, ImGuiItemStatusFlags, ImGuiInputSource, ImGuiWindow
|
||||
#include "imgui_te_engine.h" // ImGuiTestStatus, ImGuiTestRunFlags, ImGuiTestActiveFunc, ImGuiTestItemInfo, ImGuiTestLogFlags
|
||||
|
||||
/*
|
||||
|
||||
Index of this file:
|
||||
// [SECTION] Header mess, warnings
|
||||
// [SECTION] Forward declarations
|
||||
// [SECTION] ImGuiTestRef
|
||||
// [SECTION] Helper keys
|
||||
// [SECTION] ImGuiTestContext related Flags/Enumerations
|
||||
// [SECTION] ImGuiTestGenericVars, ImGuiTestGenericItemStatus
|
||||
// [SECTION] ImGuiTestContext
|
||||
// [SECTION] Debugging macros: IM_SUSPEND_TESTFUNC()
|
||||
// [SECTION] Testing/Checking macros: IM_CHECK(), IM_ERRORF() etc.
|
||||
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Header mess, warnings
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Undo some of the damage done by <windows.h>
|
||||
#ifdef Yield
|
||||
#undef Yield
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Forward declarations
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// This file
|
||||
typedef int ImGuiTestOpFlags; // Flags: See ImGuiTestOpFlags_
|
||||
|
||||
// External: imgui
|
||||
struct ImGuiDockNode;
|
||||
struct ImGuiTabBar;
|
||||
struct ImGuiWindow;
|
||||
|
||||
// External: test engine
|
||||
struct ImGuiTest; // A test registered with IM_REGISTER_TEST()
|
||||
struct ImGuiTestEngine; // Test Engine Instance (opaque)
|
||||
struct ImGuiTestEngineIO; // Test Engine IO structure (configuration flags, state)
|
||||
struct ImGuiTestItemInfo; // Information gathered about an item: label, status, bounding box etc.
|
||||
struct ImGuiTestItemList; // Result of an GatherItems() query
|
||||
struct ImGuiTestInputs; // Test Engine Simulated Inputs structure (opaque)
|
||||
struct ImGuiTestGatherTask; // Test Engine task for scanning/finding items
|
||||
struct ImGuiCaptureArgs; // Parameters for ctx->CaptureXXX functions
|
||||
enum ImGuiTestVerboseLevel : int;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] ImGuiTestRef
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Weak reference to an Item/Window given an hashed ID _or_ a string path ID.
|
||||
// This is most often passed as argument to function and generally has a very short lifetime.
|
||||
// Documentation: https://github.com/ocornut/imgui_test_engine/wiki/Named-References
|
||||
// (SUGGESTION: add those constructors to "VA Step Filter" (Visual Assist) or a .natstepfilter file (Visual Studio) so they are skipped by F11 (StepInto)
|
||||
struct IMGUI_API ImGuiTestRef
|
||||
{
|
||||
ImGuiID ID; // Pre-hashed ID
|
||||
const char* Path; // Relative or absolute path (string pointed to, not owned, as our lifetime is very short)
|
||||
|
||||
ImGuiTestRef() { ID = 0; Path = NULL; }
|
||||
ImGuiTestRef(ImGuiID id) { ID = id; Path = NULL; }
|
||||
ImGuiTestRef(const char* path) { ID = 0; Path = path; }
|
||||
bool IsEmpty() const { return ID == 0 && (Path == NULL || Path[0] == 0); }
|
||||
};
|
||||
|
||||
// Debug helper to output a string showing the Path, ID or Debug Label based on what is available (some items only have ID as we couldn't find/store a Path)
|
||||
// (The size is arbitrary, this is only used for logging info the user/debugger)
|
||||
struct IMGUI_API ImGuiTestRefDesc
|
||||
{
|
||||
char Buf[80];
|
||||
|
||||
const char* c_str() { return Buf; }
|
||||
ImGuiTestRefDesc(const ImGuiTestRef& ref, const ImGuiTestItemInfo* item);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] ImGuiTestContext related Flags/Enumerations
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Named actions. Generally you will call the named helpers e.g. ItemClick(). This is used by shared/low-level functions such as ItemAction().
|
||||
enum ImGuiTestAction
|
||||
{
|
||||
ImGuiTestAction_Unknown = 0,
|
||||
ImGuiTestAction_Hover, // Move mouse
|
||||
ImGuiTestAction_Click, // Move mouse and click
|
||||
ImGuiTestAction_DoubleClick, // Move mouse and double-click
|
||||
ImGuiTestAction_Check, // Check item if unchecked (Checkbox, MenuItem or any widget reporting ImGuiItemStatusFlags_Checkable)
|
||||
ImGuiTestAction_Uncheck, // Uncheck item if checked
|
||||
ImGuiTestAction_Open, // Open item if closed (TreeNode, BeginMenu or any widget reporting ImGuiItemStatusFlags_Openable)
|
||||
ImGuiTestAction_Close, // Close item if opened
|
||||
ImGuiTestAction_Input, // Start text inputing into a field (e.g. CTRL+Click on Drags/Slider, click on InputText etc.)
|
||||
ImGuiTestAction_NavActivate, // Activate item with navigation
|
||||
ImGuiTestAction_COUNT
|
||||
};
|
||||
|
||||
// Generic flags for many ImGuiTestContext functions
|
||||
enum ImGuiTestOpFlags_
|
||||
{
|
||||
ImGuiTestOpFlags_None = 0,
|
||||
ImGuiTestOpFlags_NoCheckHoveredId = 1 << 1, // Don't check for HoveredId after aiming for a widget. A few situations may want this: while e.g. dragging or another items prevents hovering, or for items that don't use ItemHoverable()
|
||||
ImGuiTestOpFlags_NoError = 1 << 2, // Don't abort/error e.g. if the item cannot be found or the operation doesn't succeed.
|
||||
ImGuiTestOpFlags_NoFocusWindow = 1 << 3, // Don't focus window when aiming at an item
|
||||
ImGuiTestOpFlags_NoAutoUncollapse = 1 << 4, // Disable automatically uncollapsing windows (useful when specifically testing Collapsing behaviors)
|
||||
ImGuiTestOpFlags_NoAutoOpenFullPath = 1 << 5, // Disable automatically opening intermediaries (e.g. ItemClick("Hello/OK") will automatically first open "Hello" if "OK" isn't found. Only works if ref is a string path.
|
||||
ImGuiTestOpFlags_IsSecondAttempt = 1 << 6, // Used by recursing functions to indicate a second attempt
|
||||
ImGuiTestOpFlags_MoveToEdgeL = 1 << 7, // Simple Dumb aiming helpers to test widget that care about clicking position. May need to replace will better functionalities.
|
||||
ImGuiTestOpFlags_MoveToEdgeR = 1 << 8,
|
||||
ImGuiTestOpFlags_MoveToEdgeU = 1 << 9,
|
||||
ImGuiTestOpFlags_MoveToEdgeD = 1 << 10,
|
||||
};
|
||||
|
||||
// Advanced filtering for ItemActionAll()
|
||||
struct IMGUI_API ImGuiTestActionFilter
|
||||
{
|
||||
int MaxDepth;
|
||||
int MaxPasses;
|
||||
const int* MaxItemCountPerDepth;
|
||||
ImGuiItemStatusFlags RequireAllStatusFlags;
|
||||
ImGuiItemStatusFlags RequireAnyStatusFlags;
|
||||
|
||||
ImGuiTestActionFilter() { MaxDepth = -1; MaxPasses = -1; MaxItemCountPerDepth = NULL; RequireAllStatusFlags = RequireAnyStatusFlags = 0; }
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] ImGuiTestGenericVars, ImGuiTestGenericItemStatus
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Helper struct to store various query-able state of an item.
|
||||
// This facilitate interactions between GuiFunc and TestFunc, since those state are frequently used.
|
||||
struct IMGUI_API ImGuiTestGenericItemStatus
|
||||
{
|
||||
int RetValue; // return value
|
||||
int Hovered; // result of IsItemHovered()
|
||||
int Active; // result of IsItemActive()
|
||||
int Focused; // result of IsItemFocused()
|
||||
int Clicked; // result of IsItemClicked()
|
||||
int Visible; // result of IsItemVisible()
|
||||
int Edited; // result of IsItemEdited()
|
||||
int Activated; // result of IsItemActivated()
|
||||
int Deactivated; // result of IsItemDeactivated()
|
||||
int DeactivatedAfterEdit; // result of IsItemDeactivatedAfterEdit()
|
||||
|
||||
ImGuiTestGenericItemStatus() { Clear(); }
|
||||
void Clear() { memset(this, 0, sizeof(*this)); }
|
||||
void QuerySet(bool ret_val = false) { Clear(); QueryInc(ret_val); }
|
||||
void QueryInc(bool ret_val = false) { RetValue += ret_val; Hovered += ImGui::IsItemHovered(); Active += ImGui::IsItemActive(); Focused += ImGui::IsItemFocused(); Clicked += ImGui::IsItemClicked(); Visible += ImGui::IsItemVisible(); Edited += ImGui::IsItemEdited(); Activated += ImGui::IsItemActivated(); Deactivated += ImGui::IsItemDeactivated(); DeactivatedAfterEdit += ImGui::IsItemDeactivatedAfterEdit(); }
|
||||
};
|
||||
|
||||
// Generic structure with various storage fields.
|
||||
// This is useful for tests to quickly share data between GuiFunc and TestFunc without creating custom data structure.
|
||||
// If those fields are not enough: using test->SetVarsDataType<>() + ctx->GetVars<>() it is possible to store custom data.
|
||||
struct IMGUI_API ImGuiTestGenericVars
|
||||
{
|
||||
// Generic storage with a bit of semantic to make user/test code look neater
|
||||
int Step;
|
||||
int Count;
|
||||
ImGuiID DockId;
|
||||
ImGuiID OwnerId;
|
||||
ImGuiWindowFlags WindowFlags;
|
||||
ImGuiTableFlags TableFlags;
|
||||
ImGuiPopupFlags PopupFlags;
|
||||
ImGuiTestGenericItemStatus Status;
|
||||
bool ShowWindow1, ShowWindow2;
|
||||
bool UseClipper;
|
||||
bool UseViewports;
|
||||
float Width;
|
||||
ImVec2 Pos;
|
||||
ImVec2 Size;
|
||||
ImVec2 Pivot;
|
||||
ImVec4 Color1, Color2;
|
||||
|
||||
// Generic unnamed storage
|
||||
int Int1, Int2, IntArray[10];
|
||||
float Float1, Float2, FloatArray[10];
|
||||
bool Bool1, Bool2, BoolArray[10];
|
||||
ImGuiID Id, IdArray[10];
|
||||
char Str1[256], Str2[256];
|
||||
|
||||
ImGuiTestGenericVars() { Clear(); }
|
||||
void Clear() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] ImGuiTestContext
|
||||
// Context for a running ImGuiTest
|
||||
// This is the interface that most tests will interact with.
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
struct IMGUI_API ImGuiTestContext
|
||||
{
|
||||
// User variables
|
||||
ImGuiTestGenericVars GenericVars; // Generic variables holder for convenience.
|
||||
void* UserVars = NULL; // Access using ctx->GetVars<Type>(). Setup with test->SetVarsDataType<>().
|
||||
|
||||
// Public fields
|
||||
ImGuiContext* UiContext = NULL; // UI context
|
||||
ImGuiTestEngineIO* EngineIO = NULL; // Test Engine IO/settings
|
||||
ImGuiTest* Test = NULL; // Test currently running
|
||||
ImGuiTestOutput* TestOutput = NULL; // Test output (generally == &Test->Output)
|
||||
ImGuiTestOpFlags OpFlags = ImGuiTestOpFlags_None; // Flags affecting all operation (supported: ImGuiTestOpFlags_NoAutoUncollapse)
|
||||
int PerfStressAmount = 0; // Convenience copy of engine->IO.PerfStressAmount
|
||||
int FrameCount = 0; // Test frame count (restarts from zero every time)
|
||||
int FirstTestFrameCount = 0; // First frame where TestFunc is running (after warm-up frame). This is generally -1 or 0 depending on whether we have warm up enabled
|
||||
bool FirstGuiFrame = false;
|
||||
bool HasDock = false; // #ifdef IMGUI_HAS_DOCK expressed in an easier to test value
|
||||
ImGuiCaptureArgs* CaptureArgs = NULL; // Capture settings used by ctx->Capture*() functions
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [Internal Fields]
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
ImGuiTestEngine* Engine = NULL;
|
||||
ImGuiTestInputs* Inputs = NULL;
|
||||
ImGuiTestRunFlags RunFlags = ImGuiTestRunFlags_None;
|
||||
ImGuiTestActiveFunc ActiveFunc = ImGuiTestActiveFunc_None; // None/GuiFunc/TestFunc
|
||||
double RunningTime = 0.0; // Amount of wall clock time the Test has been running. Used by safety watchdog.
|
||||
int ActionDepth = 0; // Nested depth of ctx-> function calls (used to decorate log)
|
||||
int CaptureCounter = 0; // Number of captures
|
||||
int ErrorCounter = 0; // Number of errors (generally this maxxes at 1 as most functions will early out)
|
||||
bool Abort = false;
|
||||
double PerfRefDt = -1.0;
|
||||
int PerfIterations = 400; // Number of frames for PerfCapture() measurements
|
||||
char RefStr[256] = { 0 }; // Reference window/path over which all named references are based
|
||||
ImGuiID RefID = 0; // Reference ID over which all named references are based
|
||||
ImGuiID RefWindowID = 0; // ID of a window that contains RefID item
|
||||
ImGuiInputSource InputMode = ImGuiInputSource_Mouse; // Prefer interacting with mouse/keyboard/gamepad
|
||||
ImVector<char> Clipboard; // Private clipboard for the test instance
|
||||
ImVector<ImGuiWindow*> ForeignWindowsToHide;
|
||||
ImGuiTestItemInfo DummyItemInfoNull; // Storage for ItemInfoNull()
|
||||
bool CachedLinesPrintedToTTY = false;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Public API
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Main control
|
||||
void RecoverFromUiContextErrors();
|
||||
void Finish(ImGuiTestStatus status = ImGuiTestStatus_Success); // Set test status and stop running. Usually called when running test logic from GuiFunc() only.
|
||||
ImGuiTestStatus RunChildTest(const char* test_name, ImGuiTestRunFlags flags = 0); // [Experimental] Run another test from the current test.
|
||||
template <typename T> T& GetVars() { IM_ASSERT(UserVars != NULL); return *(T*)(UserVars); }// Campanion to using t->SetVarsDataType<>(). FIXME: Assert to compare sizes
|
||||
|
||||
// Main status queries
|
||||
bool IsError() const { return TestOutput->Status == ImGuiTestStatus_Error || Abort; }
|
||||
bool IsWarmUpGuiFrame() const { return FrameCount < FirstTestFrameCount; } // Unless test->Flags has ImGuiTestFlags_NoGuiWarmUp, we run GuiFunc() twice before running TestFunc(). Those frames are called "WarmUp" frames.
|
||||
bool IsFirstGuiFrame() const { return FirstGuiFrame; }
|
||||
bool IsFirstTestFrame() const { return FrameCount == FirstTestFrameCount; } // First frame where TestFunc is running (after warm-up frame).
|
||||
bool IsGuiFuncOnly() const { return (RunFlags & ImGuiTestRunFlags_GuiFuncOnly) != 0; }
|
||||
|
||||
// Debugging
|
||||
bool SuspendTestFunc(const char* file = NULL, int line = 0); // [DEBUG] Generally called via IM_SUSPEND_TESTFUNC
|
||||
|
||||
// Logging
|
||||
void LogEx(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, ...) IM_FMTARGS(4);
|
||||
void LogExV(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, va_list args) IM_FMTLIST(4);
|
||||
void LogToTTY(ImGuiTestVerboseLevel level, const char* message, const char* message_end = NULL);
|
||||
void LogToDebugger(ImGuiTestVerboseLevel level, const char* message);
|
||||
void LogDebug(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Debug or ImGuiTestVerboseLevel_Trace depending on context depth
|
||||
void LogInfo(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Info
|
||||
void LogWarning(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Warning
|
||||
void LogError(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Error
|
||||
void LogBasicUiState();
|
||||
void LogItemList(ImGuiTestItemList* list);
|
||||
|
||||
// Yield, Timing
|
||||
void Yield(int count = 1);
|
||||
void YieldUntil(int frame_count);
|
||||
void Sleep(float time_in_second); // Sleep for a given simulation time, unless in Fast mode
|
||||
void SleepShort(); // Standard short delay of io.ActionDelayShort (~0.15f), unless in Fast mode.
|
||||
void SleepStandard(); // Standard regular delay of io.ActionDelayStandard (~0.40f), unless in Fast mode.
|
||||
void SleepNoSkip(float time_in_second, float framestep_in_second);
|
||||
|
||||
// Base Reference
|
||||
// - ItemClick("Window/Button") --> click "Window/Button"
|
||||
// - SetRef("Window"), ItemClick("Button") --> click "Window/Button"
|
||||
// - SetRef("Window"), ItemClick("/Button") --> click "Window/Button"
|
||||
// - SetRef("Window"), ItemClick("//Button") --> click "/Button"
|
||||
// - SetRef("//$FOCUSED"), ItemClick("Button") --> click "Button" in focused window.
|
||||
// See https://github.com/ocornut/imgui_test_engine/wiki/Named-References about using ImGuiTestRef in all ImGuiTestContext functions.
|
||||
// Note: SetRef() may take multiple frames to complete if specified ref is an item id.
|
||||
void SetRef(ImGuiTestRef ref);
|
||||
void SetRef(ImGuiWindow* window); // Shortcut to SetRef(window->Name) which works for ChildWindow (see code)
|
||||
ImGuiTestRef GetRef();
|
||||
|
||||
// Windows
|
||||
ImGuiTestItemInfo* WindowInfo(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
void WindowClose(ImGuiTestRef window_ref);
|
||||
void WindowCollapse(ImGuiTestRef window_ref, bool collapsed);
|
||||
void WindowFocus(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
void WindowBringToFront(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
void WindowMove(ImGuiTestRef window_ref, ImVec2 pos, ImVec2 pivot = ImVec2(0.0f, 0.0f), ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
void WindowResize(ImGuiTestRef window_ref, ImVec2 sz);
|
||||
bool WindowTeleportToMakePosVisible(ImGuiTestRef window_ref, ImVec2 pos_in_window);
|
||||
ImGuiWindow*GetWindowByRef(ImGuiTestRef window_ref);
|
||||
|
||||
// Popups
|
||||
void PopupCloseOne();
|
||||
void PopupCloseAll();
|
||||
ImGuiID PopupGetWindowID(ImGuiTestRef ref);
|
||||
|
||||
// Get hash for a decorated ID Path.
|
||||
// Note: for windows you may use WindowInfo()
|
||||
ImGuiID GetID(ImGuiTestRef ref);
|
||||
ImGuiID GetID(ImGuiTestRef ref, ImGuiTestRef seed_ref);
|
||||
|
||||
// Miscellaneous helpers
|
||||
ImVec2 GetPosOnVoid(ImGuiViewport* viewport); // Find a point that has no windows // FIXME: This needs error return and flag to enable/disable forcefully finding void.
|
||||
ImVec2 GetWindowTitlebarPoint(ImGuiTestRef window_ref); // Return a clickable point on window title-bar (window tab for docked windows).
|
||||
ImVec2 GetMainMonitorWorkPos(); // Work pos and size of main viewport when viewports are disabled, or work pos and size of monitor containing main viewport when viewports are enabled.
|
||||
ImVec2 GetMainMonitorWorkSize();
|
||||
|
||||
// Screenshot/Video Captures
|
||||
void CaptureReset(); // Reset state (use when doing multiple captures)
|
||||
void CaptureSetExtension(const char* ext); // Set capture file format (otherwise for video this default to EngineIO->VideoCaptureExtension)
|
||||
bool CaptureAddWindow(ImGuiTestRef ref); // Add window to be captured (default to capture everything)
|
||||
void CaptureScreenshotWindow(ImGuiTestRef ref, int capture_flags = 0); // Trigger a screen capture of a single window (== CaptureAddWindow() + CaptureScreenshot())
|
||||
bool CaptureScreenshot(int capture_flags = 0); // Trigger a screen capture
|
||||
bool CaptureBeginVideo(); // Start a video capture
|
||||
bool CaptureEndVideo();
|
||||
|
||||
// Mouse inputs
|
||||
void MouseMove(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
void MouseMoveToPos(ImVec2 pos);
|
||||
void MouseTeleportToPos(ImVec2 pos);
|
||||
void MouseClick(ImGuiMouseButton button = 0);
|
||||
void MouseClickMulti(ImGuiMouseButton button, int count);
|
||||
void MouseDoubleClick(ImGuiMouseButton button = 0);
|
||||
void MouseDown(ImGuiMouseButton button = 0);
|
||||
void MouseUp(ImGuiMouseButton button = 0);
|
||||
void MouseLiftDragThreshold(ImGuiMouseButton button = 0);
|
||||
void MouseDragWithDelta(ImVec2 delta, ImGuiMouseButton button = 0);
|
||||
void MouseWheel(ImVec2 delta);
|
||||
void MouseWheelX(float dx) { MouseWheel(ImVec2(dx, 0.0f)); }
|
||||
void MouseWheelY(float dy) { MouseWheel(ImVec2(0.0f, dy)); }
|
||||
void MouseMoveToVoid(ImGuiViewport* viewport = NULL);
|
||||
void MouseClickOnVoid(ImGuiMouseButton button = 0, ImGuiViewport* viewport = NULL);
|
||||
ImGuiWindow*FindHoveredWindowAtPos(const ImVec2& pos);
|
||||
bool FindExistingVoidPosOnViewport(ImGuiViewport* viewport, ImVec2* out);
|
||||
|
||||
// Mouse inputs: Viewports
|
||||
// - This is automatically called by SetRef() and any mouse action taking an item reference (e.g. ItemClick("button"), MouseClick("button"))
|
||||
// - But when using raw position directy e.g. MouseMoveToPos() / MouseTeleportToPos() without referring to the parent window before, this needs to be set.
|
||||
void MouseSetViewport(ImGuiWindow* window);
|
||||
void MouseSetViewportID(ImGuiID viewport_id);
|
||||
|
||||
// Keyboard inputs
|
||||
void KeyDown(ImGuiKeyChord key_chord);
|
||||
void KeyUp(ImGuiKeyChord key_chord);
|
||||
void KeyPress(ImGuiKeyChord key_chord, int count = 1);
|
||||
void KeyHold(ImGuiKeyChord key_chord, float time);
|
||||
void KeySetEx(ImGuiKeyChord key_chord, bool is_down, float time);
|
||||
void KeyChars(const char* chars); // Input characters
|
||||
void KeyCharsAppend(const char* chars); // Input characters at end of field
|
||||
void KeyCharsAppendEnter(const char* chars); // Input characters at end of field, press Enter
|
||||
void KeyCharsReplace(const char* chars); // Delete existing field then input characters
|
||||
void KeyCharsReplaceEnter(const char* chars); // Delete existing field then input characters, press Enter
|
||||
|
||||
// Navigation inputs
|
||||
// FIXME: Need some redesign/refactoring:
|
||||
// - This was initially intended to: replace mouse action with keyboard/gamepad
|
||||
// - Abstract keyboard vs gamepad actions
|
||||
// However this is widely inconsistent and unfinished at this point.
|
||||
void SetInputMode(ImGuiInputSource input_mode); // Mouse or Keyboard or Gamepad. In Keyboard or Gamepad mode, actions such as ItemClick or ItemInput are using nav facilities instead of Mouse.
|
||||
void NavMoveTo(ImGuiTestRef ref);
|
||||
void NavActivate(); // Activate current selected item: activate button, tweak sliders/drags. Equivalent of pressing Space on keyboard, ImGuiKey_GamepadFaceUp on a gamepad.
|
||||
void NavInput(); // Input into select item: input sliders/drags. Equivalent of pressing Enter on keyboard, ImGuiKey_GamepadFaceDown on a gamepad.
|
||||
|
||||
// Scrolling
|
||||
void ScrollTo(ImGuiTestRef ref, ImGuiAxis axis, float scroll_v, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
void ScrollToX(ImGuiTestRef ref, float scroll_x) { ScrollTo(ref, ImGuiAxis_X, scroll_x); }
|
||||
void ScrollToY(ImGuiTestRef ref, float scroll_y) { ScrollTo(ref, ImGuiAxis_Y, scroll_y); }
|
||||
void ScrollToTop(ImGuiTestRef ref);
|
||||
void ScrollToBottom(ImGuiTestRef ref);
|
||||
void ScrollToItem(ImGuiTestRef ref, ImGuiAxis axis, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
void ScrollToItemX(ImGuiTestRef ref);
|
||||
void ScrollToItemY(ImGuiTestRef ref);
|
||||
void ScrollToTabItem(ImGuiTabBar* tab_bar, ImGuiID tab_id);
|
||||
bool ScrollErrorCheck(ImGuiAxis axis, float expected, float actual, int* remaining_attempts);
|
||||
void ScrollVerifyScrollMax(ImGuiTestRef ref);
|
||||
|
||||
// Low-level queries
|
||||
// - ItemInfo queries never returns a NULL pointer, instead they return an empty instance (info->IsEmpty(), info->ID == 0) and set contexted as errored.
|
||||
// - You can use ImGuiTestOpFlags_NoError to do a query without marking context as errored. This is what ItemExists() does.
|
||||
ImGuiTestItemInfo* ItemInfo(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
ImGuiTestItemInfo* ItemInfoOpenFullPath(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
ImGuiID ItemInfoHandleWildcardSearch(const char* wildcard_prefix_start, const char* wildcard_prefix_end, const char* wildcard_suffix_start);
|
||||
ImGuiTestItemInfo* ItemInfoNull();
|
||||
void GatherItems(ImGuiTestItemList* out_list, ImGuiTestRef parent, int depth = -1);
|
||||
|
||||
// Item/Widgets manipulation
|
||||
void ItemAction(ImGuiTestAction action, ImGuiTestRef ref, ImGuiTestOpFlags flags = 0, void* action_arg = NULL);
|
||||
void ItemClick(ImGuiTestRef ref, ImGuiMouseButton button = 0, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Click, ref, flags, (void*)(size_t)button); }
|
||||
void ItemDoubleClick(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_DoubleClick, ref, flags); }
|
||||
void ItemCheck(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Check, ref, flags); }
|
||||
void ItemUncheck(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Uncheck, ref, flags); }
|
||||
void ItemOpen(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Open, ref, flags); }
|
||||
void ItemClose(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Close, ref, flags); }
|
||||
void ItemInput(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Input, ref, flags); }
|
||||
void ItemNavActivate(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_NavActivate, ref, flags); }
|
||||
bool ItemOpenFullPath(ImGuiTestRef);
|
||||
|
||||
// Item/Widgets: Batch actions over an entire scope
|
||||
void ItemActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent, const ImGuiTestActionFilter* filter = NULL);
|
||||
void ItemOpenAll(ImGuiTestRef ref_parent, int depth = -1, int passes = -1);
|
||||
void ItemCloseAll(ImGuiTestRef ref_parent, int depth = -1, int passes = -1);
|
||||
|
||||
// Item/Widgets: Helpers to easily set a value
|
||||
void ItemInputValue(ImGuiTestRef ref, int v);
|
||||
void ItemInputValue(ImGuiTestRef ref, float f);
|
||||
void ItemInputValue(ImGuiTestRef ref, const char* str);
|
||||
|
||||
// Item/Widgets: Drag and Mouse operations
|
||||
void ItemHold(ImGuiTestRef ref, float time);
|
||||
void ItemHoldForFrames(ImGuiTestRef ref, int frames);
|
||||
void ItemDragOverAndHold(ImGuiTestRef ref_src, ImGuiTestRef ref_dst);
|
||||
void ItemDragAndDrop(ImGuiTestRef ref_src, ImGuiTestRef ref_dst, ImGuiMouseButton button = 0);
|
||||
void ItemDragWithDelta(ImGuiTestRef ref_src, ImVec2 pos_delta);
|
||||
|
||||
// Item/Widgets: Status query
|
||||
bool ItemExists(ImGuiTestRef ref);
|
||||
bool ItemIsChecked(ImGuiTestRef ref);
|
||||
bool ItemIsOpened(ImGuiTestRef ref);
|
||||
void ItemVerifyCheckedIfAlive(ImGuiTestRef ref, bool checked);
|
||||
|
||||
// Helpers for Tab Bars widgets
|
||||
void TabClose(ImGuiTestRef ref);
|
||||
bool TabBarCompareOrder(ImGuiTabBar* tab_bar, const char** tab_order);
|
||||
|
||||
// Helpers for MenuBar and Menus widgets
|
||||
// - e.g. MenuCheck("File/Options/Enable grid");
|
||||
void MenuAction(ImGuiTestAction action, ImGuiTestRef ref);
|
||||
void MenuActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent);
|
||||
void MenuClick(ImGuiTestRef ref) { MenuAction(ImGuiTestAction_Click, ref); }
|
||||
void MenuCheck(ImGuiTestRef ref) { MenuAction(ImGuiTestAction_Check, ref); }
|
||||
void MenuUncheck(ImGuiTestRef ref) { MenuAction(ImGuiTestAction_Uncheck, ref); }
|
||||
void MenuCheckAll(ImGuiTestRef ref_parent) { MenuActionAll(ImGuiTestAction_Check, ref_parent); }
|
||||
void MenuUncheckAll(ImGuiTestRef ref_parent) { MenuActionAll(ImGuiTestAction_Uncheck, ref_parent); }
|
||||
|
||||
// Helpers for Combo Boxes
|
||||
void ComboClick(ImGuiTestRef ref);
|
||||
void ComboClickAll(ImGuiTestRef ref);
|
||||
|
||||
// Helpers for Tables
|
||||
void TableOpenContextMenu(ImGuiTestRef ref, int column_n = -1);
|
||||
ImGuiSortDirection TableClickHeader(ImGuiTestRef ref, const char* label, ImGuiKeyChord key_mods = 0);
|
||||
void TableSetColumnEnabled(ImGuiTestRef ref, const char* label, bool enabled);
|
||||
void TableResizeColumn(ImGuiTestRef ref, int column_n, float width);
|
||||
const ImGuiTableSortSpecs* TableGetSortSpecs(ImGuiTestRef ref);
|
||||
|
||||
// Viewports
|
||||
// IMPORTANT: Those function may alter Platform state (unless using the "Mock Viewport" backend). Use carefully.
|
||||
// Those are mostly useful to simulate OS actions and testing of viewport-specific features, may not be useful to most users.
|
||||
#ifdef IMGUI_HAS_VIEWPORT
|
||||
//void ViewportPlatform_SetWindowPos(ImGuiViewport* viewport, const ImVec2& pos);
|
||||
//void ViewportPlatform_SetWindowSize(ImGuiViewport* viewport, const ImVec2& size);
|
||||
void ViewportPlatform_SetWindowFocus(ImGuiViewport* viewport);
|
||||
void ViewportPlatform_CloseWindow(ImGuiViewport* viewport);
|
||||
#endif
|
||||
|
||||
// Docking
|
||||
#ifdef IMGUI_HAS_DOCK
|
||||
void DockClear(const char* window_name, ...);
|
||||
void DockInto(ImGuiTestRef src_id, ImGuiTestRef dst_id, ImGuiDir split_dir = ImGuiDir_None, bool is_outer_docking = false, ImGuiTestOpFlags flags = 0);
|
||||
void UndockNode(ImGuiID dock_id);
|
||||
void UndockWindow(const char* window_name);
|
||||
bool WindowIsUndockedOrStandalone(ImGuiWindow* window);
|
||||
bool DockIdIsUndockedOrStandalone(ImGuiID dock_id);
|
||||
void DockNodeHideTabBar(ImGuiDockNode* node, bool hidden);
|
||||
#endif
|
||||
|
||||
// Performances Measurement (use along with Dear ImGui Perf Tool)
|
||||
void PerfCalcRef();
|
||||
void PerfCapture(const char* category = NULL, const char* test_name = NULL, const char* csv_file = NULL);
|
||||
|
||||
// Obsolete functions
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
// Obsoleted 2022/10/11
|
||||
ImGuiID GetIDByInt(int n); // Prefer using "$$123"
|
||||
ImGuiID GetIDByInt(int n, ImGuiTestRef seed_ref);
|
||||
ImGuiID GetIDByPtr(void* p); // Prefer using "$$(ptr)0xFFFFFFFF"
|
||||
ImGuiID GetIDByPtr(void* p, ImGuiTestRef seed_ref);
|
||||
// Obsoleted 2022/09/26
|
||||
void KeyModDown(ImGuiModFlags mods) { KeyDown(mods); }
|
||||
void KeyModUp(ImGuiModFlags mods) { KeyUp(mods); }
|
||||
void KeyModPress(ImGuiModFlags mods) { KeyPress(mods); }
|
||||
#endif
|
||||
|
||||
// [Internal]
|
||||
// FIXME: Aim to remove this system...
|
||||
void ForeignWindowsHideOverPos(ImVec2 pos, ImGuiWindow** ignore_list);
|
||||
void ForeignWindowsUnhideAll();
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Debugging macros (IM_SUSPEND_TESTFUNC)
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Debug: Temporarily suspend TestFunc to let user interactively inspect the GUI state (user will need to press the "Continue" button to resume TestFunc execution)
|
||||
#define IM_SUSPEND_TESTFUNC() do { if (ctx->SuspendTestFunc(__FILE__, __LINE__)) return; } while (0)
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Testing/Checking macros: IM_CHECK(), IM_ERRORF() etc.
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Helpers used by IM_CHECK_OP() macros.
|
||||
// ImGuiTestEngine_GetTempStringBuilder() returns a shared instance of ImGuiTextBuffer to recycle memory allocations
|
||||
template<typename T> void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, T v) { buf->append("???"); IM_UNUSED(v); } // FIXME-TESTS: Could improve with some template magic
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, const char* v) { buf->appendf("\"%s\"", v); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, bool v) { buf->append(v ? "true" : "false"); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS8 v) { buf->appendf("%d", v); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU8 v) { buf->appendf("%u", v); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS16 v) { buf->appendf("%hd", v); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU16 v) { buf->appendf("%hu", v); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS32 v) { buf->appendf("%d", v); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU32 v) { buf->appendf("0x%08X", v); } // Assuming ImGuiID
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS64 v) { buf->appendf("%lld", v); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU64 v) { buf->appendf("%llu", v); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, float v) { buf->appendf("%.3f", v); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, double v) { buf->appendf("%f", v); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImVec2 v) { buf->appendf("(%.3f, %.3f)", v.x, v.y); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, const void* v) { buf->appendf("%p", v); }
|
||||
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImGuiWindow* v){ if (v) buf->appendf("\"%s\"", v->Name); else buf->append("NULL"); }
|
||||
|
||||
// We embed every macro in a do {} while(0) statement as a trick to allow using them as regular single statement, e.g. if (XXX) IM_CHECK(A); else IM_CHECK(B)
|
||||
// We leave the IM_DEBUG_BREAK() outside of the check function to step out faster when using a debugger. It also has the benefit of being lighter than an IM_ASSERT().
|
||||
#define IM_CHECK(_EXPR) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return; } while (0)
|
||||
#define IM_CHECK_NO_RET(_EXPR) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR)) { IM_DEBUG_BREAK(); } } while (0)
|
||||
#define IM_CHECK_SILENT(_EXPR) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_SilentSuccess, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return; } while (0)
|
||||
#define IM_CHECK_RETV(_EXPR,_RETV) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return _RETV; } while (0)
|
||||
#define IM_CHECK_SILENT_RETV(_EXPR,_RETV) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_SilentSuccess, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return _RETV; } while (0)
|
||||
#define IM_ERRORF(_FMT,...) do { if (ImGuiTestEngine_Error(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, _FMT, __VA_ARGS__)) { IM_DEBUG_BREAK(); } } while (0)
|
||||
#define IM_ERRORF_NOHDR(_FMT,...) do { if (ImGuiTestEngine_Error(NULL, NULL, 0, ImGuiTestCheckFlags_None, _FMT, __VA_ARGS__)) { IM_DEBUG_BREAK(); } } while (0)
|
||||
|
||||
// Those macros allow us to print out the values of both LHS and RHS expressions involved in a check.
|
||||
#define IM_CHECK_OP(_LHS, _RHS, _OP, _RETURN) \
|
||||
do \
|
||||
{ \
|
||||
auto __lhs = _LHS; /* Cache to avoid side effects */ \
|
||||
auto __rhs = _RHS; \
|
||||
bool __res = __lhs _OP __rhs; \
|
||||
ImGuiTextBuffer* expr_buf = ImGuiTestEngine_GetTempStringBuilder(); \
|
||||
expr_buf->append(#_LHS " ["); \
|
||||
ImGuiTestEngineUtil_appendf_auto(expr_buf, __lhs); \
|
||||
expr_buf->append("] " #_OP " " #_RHS " ["); \
|
||||
ImGuiTestEngineUtil_appendf_auto(expr_buf, __rhs); \
|
||||
expr_buf->append("]"); \
|
||||
if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, __res, expr_buf->c_str())) \
|
||||
IM_ASSERT(__res); \
|
||||
if (_RETURN && !__res) \
|
||||
return; \
|
||||
} while (0)
|
||||
|
||||
#define IM_CHECK_STR_OP(_LHS, _RHS, _OP, _RETURN, _FLAGS) \
|
||||
do \
|
||||
{ \
|
||||
bool __res; \
|
||||
if (ImGuiTestEngine_CheckStrOp(__FILE__, __func__, __LINE__, _FLAGS, #_OP, #_LHS, _LHS, #_RHS, _RHS, &__res)) \
|
||||
IM_ASSERT(__res); \
|
||||
if (_RETURN && !__res) \
|
||||
return; \
|
||||
} while (0)
|
||||
|
||||
// Scalar compares
|
||||
#define IM_CHECK_EQ(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, ==, true) // Equal
|
||||
#define IM_CHECK_NE(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, !=, true) // Not Equal
|
||||
#define IM_CHECK_LT(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, < , true) // Less Than
|
||||
#define IM_CHECK_LE(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, <=, true) // Less or Equal
|
||||
#define IM_CHECK_GT(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, > , true) // Greater Than
|
||||
#define IM_CHECK_GE(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, >=, true) // Greater or Equal
|
||||
|
||||
// Scalar compares, without return on failure
|
||||
#define IM_CHECK_EQ_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, ==, false) // Equal
|
||||
#define IM_CHECK_NE_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, !=, false) // Not Equal
|
||||
#define IM_CHECK_LT_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, < , false) // Less Than
|
||||
#define IM_CHECK_LE_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, <=, false) // Less or Equal
|
||||
#define IM_CHECK_GT_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, > , false) // Greater Than
|
||||
#define IM_CHECK_GE_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, >=, false) // Greater or Equal
|
||||
|
||||
// String compares
|
||||
#define IM_CHECK_STR_EQ(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_None)
|
||||
#define IM_CHECK_STR_NE(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, !=, true, ImGuiTestCheckFlags_None)
|
||||
#define IM_CHECK_STR_EQ_NO_RET(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, ==, false, ImGuiTestCheckFlags_None)
|
||||
#define IM_CHECK_STR_NE_NO_RET(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, !=, false, ImGuiTestCheckFlags_None)
|
||||
#define IM_CHECK_STR_EQ_SILENT(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_SilentSuccess)
|
||||
|
||||
// Floating point compares
|
||||
#define IM_CHECK_FLOAT_EQ_EPS(_LHS, _RHS) IM_CHECK_LE(ImFabs(_LHS - (_RHS)), FLT_EPSILON) // Float Equal
|
||||
#define IM_CHECK_FLOAT_NEAR(_LHS, _RHS, _EPS) IM_CHECK_LE(ImFabs(_LHS - (_RHS)), _EPS)
|
||||
#define IM_CHECK_FLOAT_NEAR_NO_RET(_LHS, _RHS, _E) IM_CHECK_LE_NO_RET(ImFabs(_LHS - (_RHS)), _E)
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
167
libs/imgui_test_engine/imgui_te_coroutine.cpp
Normal file
167
libs/imgui_test_engine/imgui_te_coroutine.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
// dear imgui test engine
|
||||
// (coroutine interface + optional implementation)
|
||||
// Read https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up
|
||||
|
||||
#include "imgui_te_coroutine.h"
|
||||
#include "imgui.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Coroutine implementation using std::thread
|
||||
// This implements a coroutine using std::thread, with a helper thread for each coroutine (with serialised execution, so threads never actually run concurrently)
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
#if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL
|
||||
|
||||
#include "imgui_te_utils.h"
|
||||
#include "thirdparty/Str/Str.h"
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
struct Coroutine_ImplStdThreadData
|
||||
{
|
||||
std::thread* Thread; // The thread this coroutine is using
|
||||
std::condition_variable StateChange; // Condition variable notified when the coroutine state changes
|
||||
std::mutex StateMutex; // Mutex to protect coroutine state
|
||||
bool CoroutineRunning; // Is the coroutine currently running? Lock StateMutex before access and notify StateChange on change
|
||||
bool CoroutineTerminated; // Has the coroutine terminated? Lock StateMutex before access and notify StateChange on change
|
||||
Str64 Name; // The name of this coroutine
|
||||
};
|
||||
|
||||
// The coroutine executing on the current thread (if it is a coroutine thread)
|
||||
static thread_local Coroutine_ImplStdThreadData* GThreadCoroutine = NULL;
|
||||
|
||||
// The main function for a coroutine thread
|
||||
static void CoroutineThreadMain(Coroutine_ImplStdThreadData* data, ImGuiTestCoroutineMainFunc func, void* ctx)
|
||||
{
|
||||
// Set our thread name
|
||||
ImThreadSetCurrentThreadDescription(data->Name.c_str());
|
||||
|
||||
// Set the thread coroutine
|
||||
GThreadCoroutine = data;
|
||||
|
||||
// Wait for initial Run()
|
||||
while (1)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(data->StateMutex);
|
||||
if (data->CoroutineRunning)
|
||||
break;
|
||||
data->StateChange.wait(lock);
|
||||
}
|
||||
|
||||
// Run user code, which will then call Yield() when it wants to yield control
|
||||
func(ctx);
|
||||
|
||||
// Mark as terminated
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data->StateMutex);
|
||||
|
||||
data->CoroutineTerminated = true;
|
||||
data->CoroutineRunning = false;
|
||||
data->StateChange.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ImGuiTestCoroutineHandle Coroutine_ImplStdThread_Create(ImGuiTestCoroutineMainFunc* func, const char* name, void* ctx)
|
||||
{
|
||||
Coroutine_ImplStdThreadData* data = new Coroutine_ImplStdThreadData();
|
||||
|
||||
data->Name = name;
|
||||
data->CoroutineRunning = false;
|
||||
data->CoroutineTerminated = false;
|
||||
data->Thread = new std::thread(CoroutineThreadMain, data, func, ctx);
|
||||
|
||||
return (ImGuiTestCoroutineHandle)data;
|
||||
}
|
||||
|
||||
static void Coroutine_ImplStdThread_Destroy(ImGuiTestCoroutineHandle handle)
|
||||
{
|
||||
Coroutine_ImplStdThreadData* data = (Coroutine_ImplStdThreadData*)handle;
|
||||
|
||||
IM_ASSERT(data->CoroutineTerminated); // The coroutine needs to run to termination otherwise it may leak all sorts of things and this will deadlock
|
||||
if (data->Thread)
|
||||
{
|
||||
data->Thread->join();
|
||||
|
||||
delete data->Thread;
|
||||
data->Thread = NULL;
|
||||
}
|
||||
|
||||
delete data;
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
// Run the coroutine until the next call to Yield(). Returns TRUE if the coroutine yielded, FALSE if it terminated (or had previously terminated)
|
||||
static bool Coroutine_ImplStdThread_Run(ImGuiTestCoroutineHandle handle)
|
||||
{
|
||||
Coroutine_ImplStdThreadData* data = (Coroutine_ImplStdThreadData*)handle;
|
||||
|
||||
// Wake up coroutine thread
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data->StateMutex);
|
||||
|
||||
if (data->CoroutineTerminated)
|
||||
return false; // Coroutine has already finished
|
||||
|
||||
data->CoroutineRunning = true;
|
||||
data->StateChange.notify_all();
|
||||
}
|
||||
|
||||
// Wait for coroutine to stop
|
||||
while (1)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(data->StateMutex);
|
||||
if (!data->CoroutineRunning)
|
||||
{
|
||||
// Breakpoint here to catch the point where we return from the coroutine
|
||||
if (data->CoroutineTerminated)
|
||||
return false; // Coroutine finished
|
||||
break;
|
||||
}
|
||||
data->StateChange.wait(lock);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Yield the current coroutine (can only be called from a coroutine)
|
||||
static void Coroutine_ImplStdThread_Yield()
|
||||
{
|
||||
IM_ASSERT(GThreadCoroutine); // This can only be called from a coroutine thread
|
||||
|
||||
Coroutine_ImplStdThreadData* data = GThreadCoroutine;
|
||||
|
||||
// Flag that we are not running any more
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data->StateMutex);
|
||||
data->CoroutineRunning = false;
|
||||
data->StateChange.notify_all();
|
||||
}
|
||||
|
||||
// At this point the thread that called RunCoroutine() will leave the "Wait for coroutine to stop" loop
|
||||
// Wait until we get started up again
|
||||
while (1)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(data->StateMutex);
|
||||
if (data->CoroutineRunning)
|
||||
break; // Breakpoint here if you want to catch the point where execution of this coroutine resumes
|
||||
data->StateChange.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiTestCoroutineInterface* Coroutine_ImplStdThread_GetInterface()
|
||||
{
|
||||
static ImGuiTestCoroutineInterface intf;
|
||||
intf.CreateFunc = Coroutine_ImplStdThread_Create;
|
||||
intf.DestroyFunc = Coroutine_ImplStdThread_Destroy;
|
||||
intf.RunFunc = Coroutine_ImplStdThread_Run;
|
||||
intf.YieldFunc = Coroutine_ImplStdThread_Yield;
|
||||
return &intf;
|
||||
}
|
||||
|
||||
#endif // #if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL
|
||||
56
libs/imgui_test_engine/imgui_te_coroutine.h
Normal file
56
libs/imgui_test_engine/imgui_te_coroutine.h
Normal file
@@ -0,0 +1,56 @@
|
||||
// dear imgui test engine
|
||||
// (coroutine interface + optional implementation)
|
||||
// Read https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef IMGUI_VERSION
|
||||
#include "imgui.h"
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Coroutine abstraction
|
||||
//------------------------------------------------------------------------
|
||||
// Coroutines should be used like this:
|
||||
// ImGuiTestCoroutineHandle handle = CoroutineCreate(<func>, <name>, <ctx>); // name being for debugging, and ctx being an arbitrary user context pointer
|
||||
// while (CoroutineRun(handle)) { <do other stuff };
|
||||
// CoroutineDestroy(handle);
|
||||
// The coroutine code itself should call CoroutineYieldFunc() whenever it wants to yield control back to the main thread.
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// An arbitrary handle used internally to represent coroutines (NULL indicates no handle)
|
||||
typedef void* ImGuiTestCoroutineHandle;
|
||||
|
||||
// A coroutine main function
|
||||
typedef void (ImGuiTestCoroutineMainFunc)(void* data);
|
||||
|
||||
// Coroutine support interface
|
||||
// Your app needs to return and implement this.
|
||||
// You can '#define IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL 1' in your imconfig file to use a default implementation using std::thread
|
||||
// Documentation: https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up
|
||||
struct IMGUI_API ImGuiTestCoroutineInterface
|
||||
{
|
||||
// Create a new coroutine
|
||||
ImGuiTestCoroutineHandle (*CreateFunc)(ImGuiTestCoroutineMainFunc* func, const char* name, void* data);
|
||||
|
||||
// Destroy a coroutine (which must have completed first)
|
||||
void (*DestroyFunc)(ImGuiTestCoroutineHandle handle);
|
||||
|
||||
// Run a coroutine until it yields or finishes, returning false if finished
|
||||
bool (*RunFunc)(ImGuiTestCoroutineHandle handle);
|
||||
|
||||
// Yield from a coroutine back to the caller, preserving coroutine state
|
||||
void (*YieldFunc)();
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Coroutine implementation using std::thread
|
||||
// The "coroutine" thread and user's main thread will always block on each other (both threads will NEVER run in parallel)
|
||||
// It is just an implementation convenience that we provide an implementation using std::thread as it is widely available/standard.
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
#if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL
|
||||
|
||||
IMGUI_API ImGuiTestCoroutineInterface* Coroutine_ImplStdThread_GetInterface();
|
||||
|
||||
#endif // #if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL
|
||||
2371
libs/imgui_test_engine/imgui_te_engine.cpp
Normal file
2371
libs/imgui_test_engine/imgui_te_engine.cpp
Normal file
File diff suppressed because it is too large
Load Diff
429
libs/imgui_test_engine/imgui_te_engine.h
Normal file
429
libs/imgui_test_engine/imgui_te_engine.h
Normal file
@@ -0,0 +1,429 @@
|
||||
// dear imgui test engine
|
||||
// (core)
|
||||
// This is the interface that your initial setup (app init, main loop) will mostly be using.
|
||||
// Actual tests will mostly use the interface of imgui_te_context.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h" // ImPool<>, ImRect, ImGuiItemStatusFlags, ImFormatString
|
||||
#include "imgui_te_utils.h" // ImFuncPtr
|
||||
#include "imgui_capture_tool.h" // ImGuiScreenCaptureFunc
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Forward Declarations
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
struct ImGuiTest; // Data for a test registered with IM_REGISTER_TEST()
|
||||
struct ImGuiTestContext; // Context while a test is running
|
||||
struct ImGuiTestCoroutineInterface; // Interface to expose coroutine functions (imgui_te_coroutine provides a default implementation for C++11 using std::thread, but you may use your own)
|
||||
struct ImGuiTestEngine; // Test engine instance
|
||||
struct ImGuiTestEngineIO; // Test engine public I/O
|
||||
struct ImGuiTestItemInfo; // Info queried from item (id, geometry, status flags, debug label)
|
||||
struct ImGuiTestItemList; // A list of items
|
||||
struct ImGuiTestInputs; // Simulated user inputs (will be fed into ImGuiIO by the test engine)
|
||||
struct ImGuiTestRunTask; // A queued test (test + runflags)
|
||||
|
||||
typedef int ImGuiTestFlags; // Flags: See ImGuiTestFlags_
|
||||
typedef int ImGuiTestCheckFlags; // Flags: See ImGuiTestCheckFlags_
|
||||
typedef int ImGuiTestLogFlags; // Flags: See ImGuiTestLogFlags_
|
||||
typedef int ImGuiTestRunFlags; // Flags: See ImGuiTestRunFlags_
|
||||
|
||||
enum ImGuiTestActiveFunc : int;
|
||||
enum ImGuiTestGroup : int;
|
||||
enum ImGuiTestRunSpeed : int;
|
||||
enum ImGuiTestStatus : int;
|
||||
enum ImGuiTestVerboseLevel : int;
|
||||
enum ImGuiTestEngineExportFormat : int;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Types
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Stored in ImGuiTestContext: where we are currently running GuiFunc or TestFunc
|
||||
enum ImGuiTestActiveFunc : int
|
||||
{
|
||||
ImGuiTestActiveFunc_None,
|
||||
ImGuiTestActiveFunc_GuiFunc,
|
||||
ImGuiTestActiveFunc_TestFunc
|
||||
};
|
||||
|
||||
enum ImGuiTestRunSpeed : int
|
||||
{
|
||||
ImGuiTestRunSpeed_Fast = 0, // Run tests as fast as possible (teleport mouse, skip delays, etc.)
|
||||
ImGuiTestRunSpeed_Normal = 1, // Run tests at human watchable speed (for debugging)
|
||||
ImGuiTestRunSpeed_Cinematic = 2, // Run tests with pauses between actions (for e.g. tutorials)
|
||||
ImGuiTestRunSpeed_COUNT
|
||||
};
|
||||
|
||||
enum ImGuiTestVerboseLevel : int
|
||||
{
|
||||
ImGuiTestVerboseLevel_Silent = 0, // -v0
|
||||
ImGuiTestVerboseLevel_Error = 1, // -v1
|
||||
ImGuiTestVerboseLevel_Warning = 2, // -v2
|
||||
ImGuiTestVerboseLevel_Info = 3, // -v3
|
||||
ImGuiTestVerboseLevel_Debug = 4, // -v4
|
||||
ImGuiTestVerboseLevel_Trace = 5,
|
||||
ImGuiTestVerboseLevel_COUNT
|
||||
};
|
||||
|
||||
// Test status (stored in ImGuiTest)
|
||||
enum ImGuiTestStatus : int
|
||||
{
|
||||
ImGuiTestStatus_Unknown = -1,
|
||||
ImGuiTestStatus_Success = 0,
|
||||
ImGuiTestStatus_Queued = 1,
|
||||
ImGuiTestStatus_Running = 2,
|
||||
ImGuiTestStatus_Error = 3,
|
||||
ImGuiTestStatus_Suspended = 4,
|
||||
ImGuiTestStatus_COUNT
|
||||
};
|
||||
|
||||
// Test group: this is mostly used to categorize tests in our testing UI. (Stored in ImGuiTest)
|
||||
enum ImGuiTestGroup : int
|
||||
{
|
||||
ImGuiTestGroup_Unknown = -1,
|
||||
ImGuiTestGroup_Tests = 0,
|
||||
ImGuiTestGroup_Perfs = 1,
|
||||
ImGuiTestGroup_COUNT
|
||||
};
|
||||
|
||||
// Flags (stored in ImGuiTest)
|
||||
enum ImGuiTestFlags_
|
||||
{
|
||||
ImGuiTestFlags_None = 0,
|
||||
ImGuiTestFlags_NoGuiWarmUp = 1 << 0, // Disable running the GUI func for 2 frames before starting test code. For tests which absolutely need to start before GuiFunc.
|
||||
ImGuiTestFlags_NoAutoFinish = 1 << 1, // By default, tests with no TestFunc (only a GuiFunc) will end after warmup. Setting this require test to call ctx->Finish().
|
||||
ImGuiTestFlags_NoRecoveryWarnings = 1 << 2 // Disable state recovery warnings (missing End/Pop calls etc.) for tests which may rely on those.
|
||||
//ImGuiTestFlags_RequireViewports = 1 << 10
|
||||
};
|
||||
|
||||
// Flags for IM_CHECK* macros.
|
||||
enum ImGuiTestCheckFlags_
|
||||
{
|
||||
ImGuiTestCheckFlags_None = 0,
|
||||
ImGuiTestCheckFlags_SilentSuccess = 1 << 0
|
||||
};
|
||||
|
||||
// Flags for ImGuiTestContext::Log* functions.
|
||||
enum ImGuiTestLogFlags_
|
||||
{
|
||||
ImGuiTestLogFlags_None = 0,
|
||||
ImGuiTestLogFlags_NoHeader = 1 << 0 // Do not display frame count and depth padding
|
||||
};
|
||||
|
||||
enum ImGuiTestRunFlags_
|
||||
{
|
||||
ImGuiTestRunFlags_None = 0,
|
||||
ImGuiTestRunFlags_GuiFuncDisable = 1 << 0, // Used internally to temporarily disable the GUI func (at the end of a test, etc)
|
||||
ImGuiTestRunFlags_GuiFuncOnly = 1 << 1, // Set when user selects "Run GUI func"
|
||||
ImGuiTestRunFlags_NoSuccessMsg = 1 << 2,
|
||||
ImGuiTestRunFlags_EnableRawInputs = 1 << 3, // Disable input submission to let test submission raw input event (in order to test e.g. IO queue)
|
||||
ImGuiTestRunFlags_RunFromGui = 1 << 4, // Test ran manually from GUI, will disable watchdog.
|
||||
ImGuiTestRunFlags_RunFromCommandLine= 1 << 5, // Test queued from command-line.
|
||||
|
||||
// Flags for ImGuiTestContext::RunChildTest()
|
||||
ImGuiTestRunFlags_NoError = 1 << 10,
|
||||
ImGuiTestRunFlags_ShareVars = 1 << 11, // Share generic vars and custom vars between child and parent tests (custom vars need to be same type)
|
||||
ImGuiTestRunFlags_ShareTestContext = 1 << 12, // Share ImGuiTestContext instead of creating a new one (unsure what purpose this may be useful for yet)
|
||||
// TODO: Add GuiFunc options
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Functions
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Hooks for core imgui/ library (generally called via macros)
|
||||
extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data);
|
||||
#if IMGUI_VERSION_NUM < 18934
|
||||
extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, const ImRect& bb, ImGuiID id);
|
||||
#endif
|
||||
#ifdef IMGUI_HAS_IMSTR
|
||||
extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, ImStrv label, ImGuiItemStatusFlags flags);
|
||||
#else
|
||||
extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags);
|
||||
#endif
|
||||
extern void ImGuiTestEngineHook_Log(ImGuiContext* ui_ctx, const char* fmt, ...);
|
||||
extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ui_ctx, ImGuiID id);
|
||||
|
||||
// Functions (generally called via IM_CHECK() macros)
|
||||
IMGUI_API bool ImGuiTestEngine_Check(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, bool result, const char* expr);
|
||||
IMGUI_API bool ImGuiTestEngine_CheckStrOp(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* op, const char* lhs_var, const char* lhs_value, const char* rhs_var, const char* rhs_value, bool* out_result);
|
||||
IMGUI_API bool ImGuiTestEngine_Error(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* fmt, ...);
|
||||
IMGUI_API void ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* function, int line);
|
||||
IMGUI_API ImGuiTextBuffer* ImGuiTestEngine_GetTempStringBuilder();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ImGuiTestEngine API
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Functions: Initialization
|
||||
IMGUI_API ImGuiTestEngine* ImGuiTestEngine_CreateContext(); // Create test engine
|
||||
IMGUI_API void ImGuiTestEngine_DestroyContext(ImGuiTestEngine* engine); // Destroy test engine. Call after ImGui::DestroyContext() so test engine specific ini data gets saved.
|
||||
IMGUI_API void ImGuiTestEngine_Start(ImGuiTestEngine* engine, ImGuiContext* ui_ctx); // Bind to a dear imgui context. Start coroutine.
|
||||
IMGUI_API void ImGuiTestEngine_Stop(ImGuiTestEngine* engine); // Stop coroutine and export if any. (Unbind will lazily happen on context shutdown)
|
||||
IMGUI_API void ImGuiTestEngine_PostSwap(ImGuiTestEngine* engine); // Call every frame after framebuffer swap, will process screen capture and call test_io.ScreenCaptureFunc()
|
||||
IMGUI_API ImGuiTestEngineIO& ImGuiTestEngine_GetIO(ImGuiTestEngine* engine);
|
||||
|
||||
// Macros: Register Test
|
||||
#define IM_REGISTER_TEST(_ENGINE, _CATEGORY, _NAME) ImGuiTestEngine_RegisterTest(_ENGINE, _CATEGORY, _NAME, __FILE__, __LINE__)
|
||||
IMGUI_API ImGuiTest* ImGuiTestEngine_RegisterTest(ImGuiTestEngine* engine, const char* category, const char* name, const char* src_file = NULL, int src_line = 0); // Prefer calling IM_REGISTER_TEST()
|
||||
|
||||
// Functions: Main
|
||||
IMGUI_API void ImGuiTestEngine_QueueTest(ImGuiTestEngine* engine, ImGuiTest* test, ImGuiTestRunFlags run_flags = 0);
|
||||
IMGUI_API void ImGuiTestEngine_QueueTests(ImGuiTestEngine* engine, ImGuiTestGroup group, const char* filter = NULL, ImGuiTestRunFlags run_flags = 0);
|
||||
IMGUI_API bool ImGuiTestEngine_TryAbortEngine(ImGuiTestEngine* engine);
|
||||
IMGUI_API void ImGuiTestEngine_AbortCurrentTest(ImGuiTestEngine* engine);
|
||||
IMGUI_API ImGuiTest* ImGuiTestEngine_FindTestByName(ImGuiTestEngine* engine, const char* category, const char* name);
|
||||
|
||||
// Functions: Status Queries
|
||||
// FIXME: Clarify API to avoid function calls vs raw bools in ImGuiTestEngineIO
|
||||
IMGUI_API bool ImGuiTestEngine_IsTestQueueEmpty(ImGuiTestEngine* engine);
|
||||
IMGUI_API bool ImGuiTestEngine_IsUsingSimulatedInputs(ImGuiTestEngine* engine);
|
||||
IMGUI_API void ImGuiTestEngine_GetResult(ImGuiTestEngine* engine, int& count_tested, int& success_count);
|
||||
IMGUI_API void ImGuiTestEngine_GetTestList(ImGuiTestEngine* engine, ImVector<ImGuiTest*>* out_tests);
|
||||
IMGUI_API void ImGuiTestEngine_GetTestQueue(ImGuiTestEngine* engine, ImVector<ImGuiTestRunTask>* out_tests);
|
||||
|
||||
// Functions: Crash Handling
|
||||
// Ensure past test results are properly exported even if application crash during a test.
|
||||
IMGUI_API void ImGuiTestEngine_InstallDefaultCrashHandler(); // Install default crash handler (if you don't have one)
|
||||
IMGUI_API void ImGuiTestEngine_CrashHandler(); // Default crash handler, should be called from a custom crash handler if such exists
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// IO structure to configure the test engine
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Function bound to right-clicking on a test and selecting "Open source" in the UI
|
||||
// - Easy: you can make this function call OS shell to "open" the file (e.g. ImOsOpenInShell() helper).
|
||||
// - Better: bind this function to a custom setup which can pass line number to a text editor (e.g. see 'imgui_test_suite/tools/win32_open_with_sublime.cmd' example)
|
||||
typedef void (ImGuiTestEngineSrcFileOpenFunc)(const char* filename, int line_no, void* user_data);
|
||||
|
||||
struct IMGUI_API ImGuiTestEngineIO
|
||||
{
|
||||
//-------------------------------------------------------------------------
|
||||
// Functions
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Options: Functions
|
||||
ImGuiTestCoroutineInterface* CoroutineFuncs = NULL; // (Required) Coroutine functions (see imgui_te_coroutines.h)
|
||||
ImFuncPtr(ImGuiTestEngineSrcFileOpenFunc) SrcFileOpenFunc = NULL; // (Optional) To open source files from test engine UI
|
||||
ImFuncPtr(ImGuiScreenCaptureFunc) ScreenCaptureFunc = NULL; // (Optional) To capture graphics output (application _MUST_ call ImGuiTestEngine_PostSwap() function after swapping is framebuffer)
|
||||
void* SrcFileOpenUserData = NULL; // (Optional) User data for SrcFileOpenFunc
|
||||
void* ScreenCaptureUserData = NULL; // (Optional) User data for ScreenCaptureFunc
|
||||
|
||||
// Options: Main
|
||||
bool ConfigSavedSettings = true; // Load/Save settings in main context .ini file.
|
||||
ImGuiTestRunSpeed ConfigRunSpeed = ImGuiTestRunSpeed_Fast; // Run tests in fast/normal/cinematic mode
|
||||
bool ConfigStopOnError = false; // Stop queued tests on test error
|
||||
bool ConfigBreakOnError = false; // Break debugger on test error by calling IM_DEBUG_BREAK()
|
||||
bool ConfigKeepGuiFunc = false; // Keep test GUI running at the end of the test
|
||||
ImGuiTestVerboseLevel ConfigVerboseLevel = ImGuiTestVerboseLevel_Warning;
|
||||
ImGuiTestVerboseLevel ConfigVerboseLevelOnError = ImGuiTestVerboseLevel_Info;
|
||||
bool ConfigLogToTTY = false;
|
||||
bool ConfigLogToDebugger = false;
|
||||
bool ConfigRestoreFocusAfterTests = true;// Restore focus back after running tests
|
||||
bool ConfigCaptureEnabled = true; // Master enable flags for capturing and saving captures. Disable to avoid e.g. lengthy saving of large PNG files.
|
||||
bool ConfigCaptureOnError = false;
|
||||
bool ConfigNoThrottle = false; // Disable vsync for performance measurement or fast test running
|
||||
bool ConfigMouseDrawCursor = true; // Enable drawing of Dear ImGui software mouse cursor when running tests
|
||||
float ConfigFixedDeltaTime = 0.0f; // Use fixed delta time instead of calculating it from wall clock
|
||||
int PerfStressAmount = 1; // Integer to scale the amount of items submitted in test
|
||||
char GitBranchName[64] = ""; // e.g. fill in branch name (e.g. recorded in perf samples .csv)
|
||||
|
||||
// Options: Speed of user simulation
|
||||
float MouseSpeed = 600.0f; // Mouse speed (pixel/second) when not running in fast mode
|
||||
float MouseWobble = 0.25f; // (0.0f..1.0f) How much wobble to apply to the mouse (pixels per pixel of move distance) when not running in fast mode
|
||||
float ScrollSpeed = 1400.0f; // Scroll speed (pixel/second) when not running in fast mode
|
||||
float TypingSpeed = 20.0f; // Char input speed (characters/second) when not running in fast mode
|
||||
float ActionDelayShort = 0.15f; // Time between short actions
|
||||
float ActionDelayStandard = 0.40f; // Time between most actions
|
||||
|
||||
// Options: Screen/video capture
|
||||
char VideoCaptureEncoderPath[256] = ""; // Video encoder executable path, e.g. "path/to/ffmpeg.exe".
|
||||
char VideoCaptureEncoderParams[256] = "";// Video encoder parameters for .MP4 captures, e.g. see IMGUI_CAPTURE_DEFAULT_VIDEO_PARAMS_FOR_FFMPEG
|
||||
char GifCaptureEncoderParams[512] = ""; // Video encoder parameters for .GIF captures, e.g. see IMGUI_CAPTURE_DEFAULT_GIF_PARAMS_FOR_FFMPEG
|
||||
char VideoCaptureExtension[8] = ".mp4"; // Video file extension (default, may be overridden by test).
|
||||
|
||||
// Options: Watchdog. Set values to FLT_MAX to disable.
|
||||
// Interactive GUI applications that may be slower tend to use higher values.
|
||||
float ConfigWatchdogWarning = 30.0f; // Warn when a test exceed this time (in second)
|
||||
float ConfigWatchdogKillTest = 60.0f; // Attempt to stop running a test when exceeding this time (in second)
|
||||
float ConfigWatchdogKillApp = FLT_MAX; // Stop application when exceeding this time (in second)
|
||||
|
||||
// Options: Export
|
||||
// While you can manually call ImGuiTestEngine_Export(), registering filename/format here ensure the crash handler will always export if application crash.
|
||||
const char* ExportResultsFilename = NULL;
|
||||
ImGuiTestEngineExportFormat ExportResultsFormat = (ImGuiTestEngineExportFormat)0;
|
||||
|
||||
// Options: Sanity Checks
|
||||
bool CheckDrawDataIntegrity = false; // Check ImDrawData integrity (buffer count, etc.). Currently cheap but may become a slow operation.
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Output
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Output: State of test engine
|
||||
bool IsRunningTests = false;
|
||||
bool IsRequestingMaxAppSpeed = false; // When running in fast mode: request app to skip vsync or even skip rendering if it wants
|
||||
bool IsCapturing = false; // Capture is in progress
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ImGuiTestItemInfo
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Information about a given item or window, result of an ItemInfo() or WindowInfo() query
|
||||
struct ImGuiTestItemInfo
|
||||
{
|
||||
int RefCount : 8; // User can increment this if they want to hold on the result pointer across frames, otherwise the task will be GC-ed.
|
||||
unsigned int NavLayer : 1; // Nav layer of the item (ImGuiNavLayer)
|
||||
int Depth : 16; // Depth from requested parent id. 0 == ID is immediate child of requested parent id.
|
||||
int TimestampMain = -1; // Timestamp of main result (all fields)
|
||||
int TimestampStatus = -1; // Timestamp of StatusFlags
|
||||
ImGuiID ID = 0; // Item ID
|
||||
ImGuiID ParentID = 0; // Item Parent ID (value at top of the ID stack)
|
||||
ImGuiWindow* Window = NULL; // Item Window
|
||||
ImRect RectFull = ImRect(); // Item Rectangle
|
||||
ImRect RectClipped = ImRect(); // Item Rectangle (clipped with window->ClipRect at time of item submission)
|
||||
ImGuiItemFlags InFlags = 0; // Item flags
|
||||
ImGuiItemStatusFlags StatusFlags = 0; // Item Status flags (fully updated for some items only, compare TimestampStatus to FrameCount)
|
||||
char DebugLabel[32] = {}; // Shortened label for debugging purpose
|
||||
|
||||
ImGuiTestItemInfo() { RefCount = 0; NavLayer = 0; Depth = 0; }
|
||||
bool IsEmpty() const { return ID == 0; }
|
||||
};
|
||||
|
||||
// Result of an GatherItems() query
|
||||
struct IMGUI_API ImGuiTestItemList
|
||||
{
|
||||
ImPool<ImGuiTestItemInfo> Pool;
|
||||
|
||||
void Clear() { Pool.Clear(); }
|
||||
void Reserve(int capacity) { Pool.Reserve(capacity); }
|
||||
int GetSize() const { return Pool.GetMapSize(); }
|
||||
const ImGuiTestItemInfo* GetByIndex(int n) { return Pool.GetByIndex(n); }
|
||||
const ImGuiTestItemInfo* GetByID(ImGuiID id) { return Pool.GetByKey(id); }
|
||||
|
||||
// For range-for
|
||||
size_t size() const { return (size_t)Pool.GetMapSize(); }
|
||||
const ImGuiTestItemInfo* begin() const { return Pool.Buf.begin(); }
|
||||
const ImGuiTestItemInfo* end() const { return Pool.Buf.end(); }
|
||||
const ImGuiTestItemInfo* operator[] (size_t n) { return &Pool.Buf[(int)n]; }
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ImGuiTestLog: store textual output of one given Test.
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
struct IMGUI_API ImGuiTestLogLineInfo
|
||||
{
|
||||
ImGuiTestVerboseLevel Level;
|
||||
int LineOffset;
|
||||
};
|
||||
|
||||
struct IMGUI_API ImGuiTestLog
|
||||
{
|
||||
ImGuiTextBuffer Buffer;
|
||||
ImVector<ImGuiTestLogLineInfo> LineInfo;
|
||||
int CountPerLevel[ImGuiTestVerboseLevel_COUNT] = {};
|
||||
|
||||
// Functions
|
||||
ImGuiTestLog() {}
|
||||
bool IsEmpty() const { return Buffer.empty(); }
|
||||
void Clear();
|
||||
|
||||
// Extract log contents filtered per log-level.
|
||||
// Output:
|
||||
// - If 'buffer != NULL': all extracted lines are appended to 'buffer'. Use 'buffer->c_str()' on your side to obtain the text.
|
||||
// - Return value: number of lines extracted (should be equivalent to number of '\n' inside buffer->c_str()).
|
||||
// - You may call the function with buffer == NULL to only obtain a count without getting the data.
|
||||
// Verbose levels are inclusive:
|
||||
// - To get ONLY Error: Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Error
|
||||
// - To get ONLY Error and Warnings: Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Warning
|
||||
// - To get All Errors, Warnings, Debug... Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Trace
|
||||
int ExtractLinesForVerboseLevels(ImGuiTestVerboseLevel level_min, ImGuiTestVerboseLevel level_max, ImGuiTextBuffer* out_buffer);
|
||||
|
||||
// [Internal]
|
||||
void UpdateLineOffsets(ImGuiTestEngineIO* engine_io, ImGuiTestVerboseLevel level, const char* start);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ImGuiTest
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
typedef void (ImGuiTestGuiFunc)(ImGuiTestContext* ctx);
|
||||
typedef void (ImGuiTestTestFunc)(ImGuiTestContext* ctx);
|
||||
|
||||
// Wraps a placement new of a given type (where 'buffer' is the allocated memory)
|
||||
typedef void (ImGuiTestVarsConstructor)(void* buffer);
|
||||
typedef void (ImGuiTestVarsPostConstructor)(ImGuiTestContext* ctx, void* ptr, void* fn);
|
||||
typedef void (ImGuiTestVarsDestructor)(void* ptr);
|
||||
|
||||
// Storage for the output of a test run
|
||||
struct IMGUI_API ImGuiTestOutput
|
||||
{
|
||||
ImGuiTestStatus Status = ImGuiTestStatus_Unknown;
|
||||
ImGuiTestLog Log;
|
||||
ImU64 StartTime = 0;
|
||||
ImU64 EndTime = 0;
|
||||
};
|
||||
|
||||
// Storage for one test
|
||||
struct IMGUI_API ImGuiTest
|
||||
{
|
||||
// Test Definition
|
||||
const char* Category = NULL; // Literal, not owned
|
||||
const char* Name = NULL; // Literal, generally not owned unless NameOwned=true
|
||||
ImGuiTestGroup Group = ImGuiTestGroup_Unknown; // Coarse groups: 'Tests' or 'Perf'
|
||||
bool NameOwned = false; //
|
||||
const char* SourceFile = NULL; // __FILE__
|
||||
int SourceLine = 0; // __LINE__
|
||||
int SourceLineEnd = 0; // Calculated by ImGuiTestEngine_StartCalcSourceLineEnds()
|
||||
int ArgVariant = 0; // User parameter. Generally we use it to run variations of a same test by sharing GuiFunc/TestFunc
|
||||
ImGuiTestFlags Flags = ImGuiTestFlags_None; // See ImGuiTestFlags_
|
||||
ImFuncPtr(ImGuiTestGuiFunc) GuiFunc = NULL; // GUI function (optional if your test are running over an existing GUI application)
|
||||
ImFuncPtr(ImGuiTestTestFunc) TestFunc = NULL; // Test function
|
||||
void* UserData = NULL; // General purpose user data (if assigning capturing lambdas on GuiFunc/TestFunc you may not need to use this)
|
||||
//ImVector<ImGuiTestRunTask> Dependencies; // Registered via AddDependencyTest(), ran automatically before our test. This is a simpler wrapper to calling ctx->RunChildTest()
|
||||
|
||||
// Last Test Output/Status
|
||||
// (this is the only part that may change after registration)
|
||||
ImGuiTestOutput Output;
|
||||
|
||||
// User variables (which are instantiated when running the test)
|
||||
// Setup after test registration with SetVarsDataType<>(), access instance during test with GetVars<>().
|
||||
// This is mostly useful to communicate between GuiFunc and TestFunc. If you don't use both you may not want to use it!
|
||||
size_t VarsSize = 0;
|
||||
ImGuiTestVarsConstructor* VarsConstructor = NULL;
|
||||
ImGuiTestVarsPostConstructor* VarsPostConstructor = NULL; // To override constructor default (in case the default are problematic on the first GuiFunc frame)
|
||||
void* VarsPostConstructorUserFn = NULL;
|
||||
ImGuiTestVarsDestructor* VarsDestructor = NULL;
|
||||
|
||||
// Functions
|
||||
ImGuiTest() {}
|
||||
~ImGuiTest();
|
||||
|
||||
void SetOwnedName(const char* name);
|
||||
|
||||
template <typename T>
|
||||
void SetVarsDataType(void(*post_initialize)(ImGuiTestContext* ctx, T& vars) = NULL)
|
||||
{
|
||||
VarsSize = sizeof(T);
|
||||
VarsConstructor = [](void* ptr) { IM_PLACEMENT_NEW(ptr) T; };
|
||||
VarsDestructor = [](void* ptr) { IM_UNUSED(ptr); reinterpret_cast<T*>(ptr)->~T(); };
|
||||
if (post_initialize != NULL)
|
||||
{
|
||||
VarsPostConstructorUserFn = (void*)post_initialize;
|
||||
VarsPostConstructor = [](ImGuiTestContext* ctx, void* ptr, void* fn) { ((void (*)(ImGuiTestContext*, T&))(fn))(ctx, *(T*)ptr); };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Stored in test queue
|
||||
struct IMGUI_API ImGuiTestRunTask
|
||||
{
|
||||
ImGuiTest* Test = NULL;
|
||||
ImGuiTestRunFlags RunFlags = ImGuiTestRunFlags_None;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
303
libs/imgui_test_engine/imgui_te_exporters.cpp
Normal file
303
libs/imgui_test_engine/imgui_te_exporters.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
// dear imgui test engine
|
||||
// (result exporters)
|
||||
// Read https://github.com/ocornut/imgui_test_engine/wiki/Exporting-Results
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#include "imgui_te_exporters.h"
|
||||
#include "imgui_te_engine.h"
|
||||
#include "imgui_te_internal.h"
|
||||
#include "thirdparty/Str/Str.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] FORWARD DECLARATIONS
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
static void ImGuiTestEngine_ExportJUnitXml(ImGuiTestEngine* engine, const char* output_file);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] TEST ENGINE EXPORTER FUNCTIONS
|
||||
//-------------------------------------------------------------------------
|
||||
// - ImGuiTestEngine_PrintResultSummary()
|
||||
// - ImGuiTestEngine_Export()
|
||||
// - ImGuiTestEngine_ExportEx()
|
||||
// - ImGuiTestEngine_ExportJUnitXml()
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void ImGuiTestEngine_PrintResultSummary(ImGuiTestEngine* engine)
|
||||
{
|
||||
int count_tested = 0;
|
||||
int count_success = 0;
|
||||
ImGuiTestEngine_GetResult(engine, count_tested, count_success);
|
||||
|
||||
if (count_success < count_tested)
|
||||
{
|
||||
printf("\nFailing tests:\n");
|
||||
for (ImGuiTest* test : engine->TestsAll)
|
||||
if (test->Output.Status == ImGuiTestStatus_Error)
|
||||
printf("- %s\n", test->Name);
|
||||
}
|
||||
|
||||
ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, (count_success == count_tested) ? ImOsConsoleTextColor_BrightGreen : ImOsConsoleTextColor_BrightRed);
|
||||
printf("\nTests Result: %s\n", (count_success == count_tested) ? "OK" : "Errors");
|
||||
printf("(%d/%d tests passed)\n", count_success, count_tested);
|
||||
ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_White);
|
||||
}
|
||||
|
||||
// This is mostly a copy of ImGuiTestEngine_PrintResultSummary with few additions.
|
||||
static void ImGuiTestEngine_ExportResultSummary(ImGuiTestEngine* engine, FILE* fp, int indent_count, ImGuiTestGroup group)
|
||||
{
|
||||
int count_tested = 0;
|
||||
int count_success = 0;
|
||||
|
||||
for (ImGuiTest* test : engine->TestsAll)
|
||||
{
|
||||
if (test->Group != group)
|
||||
continue;
|
||||
if (test->Output.Status != ImGuiTestStatus_Unknown)
|
||||
count_tested++;
|
||||
if (test->Output.Status == ImGuiTestStatus_Success)
|
||||
count_success++;
|
||||
}
|
||||
|
||||
Str64 indent_str;
|
||||
indent_str.reserve(indent_count + 1);
|
||||
memset(indent_str.c_str(), ' ', indent_count);
|
||||
indent_str[indent_count] = 0;
|
||||
const char* indent = indent_str.c_str();
|
||||
|
||||
if (count_success < count_tested)
|
||||
{
|
||||
fprintf(fp, "\n%sFailing tests:\n", indent);
|
||||
for (ImGuiTest* test : engine->TestsAll)
|
||||
{
|
||||
if (test->Group != group)
|
||||
continue;
|
||||
if (test->Output.Status == ImGuiTestStatus_Error)
|
||||
fprintf(fp, "%s- %s\n", indent, test->Name);
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
|
||||
fprintf(fp, "%sTests Result: %s\n", indent, (count_success == count_tested) ? "OK" : "Errors");
|
||||
fprintf(fp, "%s(%d/%d tests passed)\n", indent, count_success, count_tested);
|
||||
}
|
||||
|
||||
static bool ImGuiTestEngine_HasAnyLogLines(ImGuiTestLog* test_log, ImGuiTestVerboseLevel level)
|
||||
{
|
||||
for (auto& line_info : test_log->LineInfo)
|
||||
if (line_info.Level <= level)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ImGuiTestEngine_PrintLogLines(FILE* fp, ImGuiTestLog* test_log, int indent, ImGuiTestVerboseLevel level)
|
||||
{
|
||||
Str128 log_line;
|
||||
for (auto& line_info : test_log->LineInfo)
|
||||
{
|
||||
if (line_info.Level > level)
|
||||
continue;
|
||||
const char* line_start = test_log->Buffer.c_str() + line_info.LineOffset;
|
||||
const char* line_end = strstr(line_start, "\n"); // FIXME: Incorrect.
|
||||
log_line.set(line_start, line_end);
|
||||
ImStrXmlEscape(&log_line); // FIXME: Should not be here considering the function name.
|
||||
|
||||
// Some users may want to disable indenting?
|
||||
fprintf(fp, "%*s%s\n", indent, "", log_line.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Export using settings stored in ImGuiTestEngineIO
|
||||
// This is called by ImGuiTestEngine_CrashHandler().
|
||||
void ImGuiTestEngine_Export(ImGuiTestEngine* engine)
|
||||
{
|
||||
ImGuiTestEngineIO& io = engine->IO;
|
||||
ImGuiTestEngine_ExportEx(engine, io.ExportResultsFormat, io.ExportResultsFilename);
|
||||
}
|
||||
|
||||
// Export using custom settings.
|
||||
void ImGuiTestEngine_ExportEx(ImGuiTestEngine* engine, ImGuiTestEngineExportFormat format, const char* filename)
|
||||
{
|
||||
if (format == ImGuiTestEngineExportFormat_None)
|
||||
return;
|
||||
IM_ASSERT(filename != NULL);
|
||||
|
||||
if (format == ImGuiTestEngineExportFormat_JUnitXml)
|
||||
ImGuiTestEngine_ExportJUnitXml(engine, filename);
|
||||
else
|
||||
IM_ASSERT(0);
|
||||
}
|
||||
|
||||
void ImGuiTestEngine_ExportJUnitXml(ImGuiTestEngine* engine, const char* output_file)
|
||||
{
|
||||
IM_ASSERT(engine != NULL);
|
||||
IM_ASSERT(output_file != NULL);
|
||||
|
||||
FILE* fp = fopen(output_file, "w+b");
|
||||
if (fp == NULL)
|
||||
{
|
||||
fprintf(stderr, "Writing '%s' failed.\n", output_file);
|
||||
return;
|
||||
}
|
||||
|
||||
// Per-testsuite test statistics.
|
||||
struct
|
||||
{
|
||||
const char* Name = NULL;
|
||||
int Tests = 0;
|
||||
int Failures = 0;
|
||||
int Disabled = 0;
|
||||
} testsuites[ImGuiTestGroup_COUNT];
|
||||
testsuites[ImGuiTestGroup_Tests].Name = "tests";
|
||||
testsuites[ImGuiTestGroup_Perfs].Name = "perfs";
|
||||
|
||||
for (int n = 0; n < engine->TestsAll.Size; n++)
|
||||
{
|
||||
ImGuiTest* test = engine->TestsAll[n];
|
||||
auto* stats = &testsuites[test->Group];
|
||||
stats->Tests += 1;
|
||||
if (test->Output.Status == ImGuiTestStatus_Error)
|
||||
stats->Failures += 1;
|
||||
else if (test->Output.Status == ImGuiTestStatus_Unknown)
|
||||
stats->Disabled += 1;
|
||||
}
|
||||
|
||||
// Attributes for <testsuites> tag.
|
||||
const char* testsuites_name = "Dear ImGui";
|
||||
int testsuites_failures = 0;
|
||||
int testsuites_tests = 0;
|
||||
int testsuites_disabled = 0;
|
||||
float testsuites_time = (float)((double)(engine->BatchEndTime - engine->BatchStartTime) / 1000000.0);
|
||||
for (int testsuite_id = ImGuiTestGroup_Tests; testsuite_id < ImGuiTestGroup_COUNT; testsuite_id++)
|
||||
{
|
||||
testsuites_tests += testsuites[testsuite_id].Tests;
|
||||
testsuites_failures += testsuites[testsuite_id].Failures;
|
||||
testsuites_disabled += testsuites[testsuite_id].Disabled;
|
||||
}
|
||||
|
||||
// FIXME: "errors" attribute and <error> tag in <testcase> may be supported if we have means to catch unexpected errors like assertions.
|
||||
fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<testsuites disabled=\"%d\" errors=\"0\" failures=\"%d\" name=\"%s\" tests=\"%d\" time=\"%.3f\">\n",
|
||||
testsuites_disabled, testsuites_failures, testsuites_name, testsuites_tests, testsuites_time);
|
||||
|
||||
for (int testsuite_id = ImGuiTestGroup_Tests; testsuite_id < ImGuiTestGroup_COUNT; testsuite_id++)
|
||||
{
|
||||
// Attributes for <testsuite> tag.
|
||||
auto* testsuite = &testsuites[testsuite_id];
|
||||
float testsuite_time = testsuites_time; // FIXME: We do not differentiate between tests and perfs, they are executed in one big batch.
|
||||
Str30 testsuite_timestamp = "";
|
||||
ImTimestampToISO8601(engine->BatchStartTime, &testsuite_timestamp);
|
||||
fprintf(fp, " <testsuite name=\"%s\" tests=\"%d\" disabled=\"%d\" errors=\"0\" failures=\"%d\" hostname=\"\" id=\"%d\" package=\"\" skipped=\"0\" time=\"%.3f\" timestamp=\"%s\">\n",
|
||||
testsuite->Name, testsuite->Tests, testsuite->Disabled, testsuite->Failures, testsuite_id, testsuite_time, testsuite_timestamp.c_str());
|
||||
|
||||
for (int n = 0; n < engine->TestsAll.Size; n++)
|
||||
{
|
||||
ImGuiTest* test = engine->TestsAll[n];
|
||||
if (test->Group != testsuite_id)
|
||||
continue;
|
||||
|
||||
ImGuiTestOutput* test_output = &test->Output;
|
||||
ImGuiTestLog* test_log = &test_output->Log;
|
||||
|
||||
// Attributes for <testcase> tag.
|
||||
const char* testcase_name = test->Name;
|
||||
const char* testcase_classname = test->Category;
|
||||
const char* testcase_status = ImGuiTestEngine_GetStatusName(test_output->Status);
|
||||
const float testcase_time = (float)((double)(test_output->EndTime - test_output->StartTime) / 1000000.0);
|
||||
|
||||
fprintf(fp, " <testcase name=\"%s\" assertions=\"0\" classname=\"%s\" status=\"%s\" time=\"%.3f\">\n",
|
||||
testcase_name, testcase_classname, testcase_status, testcase_time);
|
||||
|
||||
if (test_output->Status == ImGuiTestStatus_Error)
|
||||
{
|
||||
// Skip last error message because it is generic information that test failed.
|
||||
Str128 log_line;
|
||||
for (int i = test_log->LineInfo.Size - 2; i >= 0; i--)
|
||||
{
|
||||
ImGuiTestLogLineInfo* line_info = &test_log->LineInfo[i];
|
||||
if (line_info->Level > engine->IO.ConfigVerboseLevelOnError)
|
||||
continue;
|
||||
if (line_info->Level == ImGuiTestVerboseLevel_Error)
|
||||
{
|
||||
const char* line_start = test_log->Buffer.c_str() + line_info->LineOffset;
|
||||
const char* line_end = strstr(line_start, "\n");
|
||||
log_line.set(line_start, line_end);
|
||||
ImStrXmlEscape(&log_line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Failing tests save their "on error" log output in text element of <failure> tag.
|
||||
fprintf(fp, " <failure message=\"%s\" type=\"error\">\n", log_line.c_str());
|
||||
ImGuiTestEngine_PrintLogLines(fp, test_log, 8, engine->IO.ConfigVerboseLevelOnError);
|
||||
fprintf(fp, " </failure>\n");
|
||||
}
|
||||
|
||||
if (test_output->Status == ImGuiTestStatus_Unknown)
|
||||
{
|
||||
fprintf(fp, " <skipped message=\"Skipped\" />\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Succeeding tests save their default log output output as "stdout".
|
||||
if (ImGuiTestEngine_HasAnyLogLines(test_log, engine->IO.ConfigVerboseLevel))
|
||||
{
|
||||
fprintf(fp, " <system-out>\n");
|
||||
ImGuiTestEngine_PrintLogLines(fp, test_log, 8, engine->IO.ConfigVerboseLevel);
|
||||
fprintf(fp, " </system-out>\n");
|
||||
}
|
||||
|
||||
// Save error messages as "stderr".
|
||||
if (ImGuiTestEngine_HasAnyLogLines(test_log, ImGuiTestVerboseLevel_Error))
|
||||
{
|
||||
fprintf(fp, " <system-err>\n");
|
||||
ImGuiTestEngine_PrintLogLines(fp, test_log, 8, ImGuiTestVerboseLevel_Error);
|
||||
fprintf(fp, " </system-err>\n");
|
||||
}
|
||||
}
|
||||
fprintf(fp, " </testcase>\n");
|
||||
}
|
||||
|
||||
if (testsuites[testsuite_id].Disabled < testsuites[testsuite_id].Tests) // Any tests executed
|
||||
{
|
||||
// Log all log messages as "stdout".
|
||||
fprintf(fp, " <system-out>\n");
|
||||
for (int n = 0; n < engine->TestsAll.Size; n++)
|
||||
{
|
||||
ImGuiTest* test = engine->TestsAll[n];
|
||||
ImGuiTestOutput* test_output = &test->Output;
|
||||
if (test->Group != testsuite_id)
|
||||
continue;
|
||||
if (test_output->Status == ImGuiTestStatus_Unknown)
|
||||
continue;
|
||||
fprintf(fp, " [0000] Test: '%s' '%s'..\n", test->Category, test->Name);
|
||||
ImGuiTestVerboseLevel level = test_output->Status == ImGuiTestStatus_Error ? engine->IO.ConfigVerboseLevelOnError : engine->IO.ConfigVerboseLevel;
|
||||
ImGuiTestEngine_PrintLogLines(fp, &test_output->Log, 6, level);
|
||||
}
|
||||
ImGuiTestEngine_ExportResultSummary(engine, fp, 6, (ImGuiTestGroup)testsuite_id);
|
||||
fprintf(fp, " </system-out>\n");
|
||||
|
||||
// Log all warning and error messages as "stderr".
|
||||
fprintf(fp, " <system-err>\n");
|
||||
for (int n = 0; n < engine->TestsAll.Size; n++)
|
||||
{
|
||||
ImGuiTest* test = engine->TestsAll[n];
|
||||
ImGuiTestOutput* test_output = &test->Output;
|
||||
if (test->Group != testsuite_id)
|
||||
continue;
|
||||
if (test_output->Status == ImGuiTestStatus_Unknown)
|
||||
continue;
|
||||
fprintf(fp, " [0000] Test: '%s' '%s'..\n", test->Category, test->Name);
|
||||
ImGuiTestEngine_PrintLogLines(fp, &test_output->Log, 6, ImGuiTestVerboseLevel_Warning);
|
||||
}
|
||||
ImGuiTestEngine_ExportResultSummary(engine, fp, 6, (ImGuiTestGroup)testsuite_id);
|
||||
fprintf(fp, " </system-err>\n");
|
||||
}
|
||||
fprintf(fp, " </testsuite>\n");
|
||||
}
|
||||
fprintf(fp, "</testsuites>\n");
|
||||
fclose(fp);
|
||||
fprintf(stdout, "Saved test results to '%s' successfully.\n", output_file);
|
||||
}
|
||||
59
libs/imgui_test_engine/imgui_te_exporters.h
Normal file
59
libs/imgui_test_engine/imgui_te_exporters.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// dear imgui test engine
|
||||
// (result exporters)
|
||||
// Read https://github.com/ocornut/imgui_test_engine/wiki/Exporting-Results
|
||||
|
||||
#pragma once
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Description
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
// Test results may be exported in one of supported formats.
|
||||
// To enable result exporting please configure test engine as follows:
|
||||
//
|
||||
// ImGuiTestEngineIO& test_io = ImGuiTestEngine_GetIO(engine);
|
||||
// test_io.ExportResultsFile = "output_file.xml";
|
||||
// test_io.ExportResultsFormat = ImGuiTestEngineExportFormat_<...>;
|
||||
//
|
||||
// JUnit XML format
|
||||
//------------------
|
||||
// JUnit XML format described at https://llg.cubic.org/docs/junit/. Many
|
||||
// third party applications support consumption of this format. Some of
|
||||
// of them are listed here:
|
||||
// - Jenkins
|
||||
// - Installation guide: https://www.jenkins.io/doc/book/installing/docker/
|
||||
// - JUnit plugin: https://plugins.jenkins.io/junit/
|
||||
// - xunit-viewer
|
||||
// - Project: https://github.com/lukejpreston/xunit-viewer
|
||||
// - Install npm: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm
|
||||
// - Install viewer and view test results:
|
||||
// npm install xunit-viewer
|
||||
// imgui_test_suite -nopause -v2 -ve4 -nogui -export-file junit.xml tests
|
||||
// node_modules/xunit-viewer/bin/xunit-viewer -r junit.xml -o junit.html
|
||||
// - Open junit.html
|
||||
//
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Forward Declarations
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
struct ImGuiTestEngine;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Types
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
enum ImGuiTestEngineExportFormat : int
|
||||
{
|
||||
ImGuiTestEngineExportFormat_None = 0,
|
||||
ImGuiTestEngineExportFormat_JUnitXml,
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Functions
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void ImGuiTestEngine_PrintResultSummary(ImGuiTestEngine* engine);
|
||||
|
||||
void ImGuiTestEngine_Export(ImGuiTestEngine* engine);
|
||||
void ImGuiTestEngine_ExportEx(ImGuiTestEngine* engine, ImGuiTestEngineExportFormat format, const char* filename);
|
||||
57
libs/imgui_test_engine/imgui_te_imconfig.h
Normal file
57
libs/imgui_test_engine/imgui_te_imconfig.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// dear imgui test engine
|
||||
// (template for compile-time configuration)
|
||||
// Replicate or #include this file in your imconfig.h to enable test engine.
|
||||
|
||||
// Compile Dear ImGui with test engine hooks
|
||||
// (Important: This is a value-less define, to be consistent with other defines used in core dear imgui.)
|
||||
#define IMGUI_ENABLE_TEST_ENGINE
|
||||
|
||||
// [Optional, default 0] Enable plotting of perflog data for comparing performance of different runs.
|
||||
// This feature requires ImPlot to be linked in the application.
|
||||
#ifndef IMGUI_TEST_ENGINE_ENABLE_IMPLOT
|
||||
#define IMGUI_TEST_ENGINE_ENABLE_IMPLOT 0
|
||||
#endif
|
||||
|
||||
// [Optional, default 1] Enable screen capture and PNG/GIF saving functionalities
|
||||
// There's not much point to disable this but we provide it to reassure user that the dependencies on imstb_image_write.h and ffmpeg are technically optional.
|
||||
#ifndef IMGUI_TEST_ENGINE_ENABLE_CAPTURE
|
||||
#define IMGUI_TEST_ENGINE_ENABLE_CAPTURE 1
|
||||
#endif
|
||||
|
||||
// [Optional, default 0] Using std::function and <functional> for function pointers such as ImGuiTest::TestFunc and ImGuiTest::GuiFunc
|
||||
#ifndef IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION
|
||||
#define IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION 0
|
||||
#endif
|
||||
|
||||
// [Optional, default 0] Automatically fill ImGuiTestEngineIO::CoroutineFuncs with a default implementation using std::thread
|
||||
#ifndef IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL
|
||||
#define IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL 0
|
||||
#endif
|
||||
|
||||
// Define IM_DEBUG_BREAK macros so it is accessible in imgui.h
|
||||
// (this is a conveniance for app using test engine may define an IM_ASSERT() that uses this instead of an actual assert)
|
||||
// (this is a copy of the block in imgui_internal.h. if the one in imgui_internal.h were to be defined at the top of imgui.h we wouldn't need this)
|
||||
#ifndef IM_DEBUG_BREAK
|
||||
#if defined (_MSC_VER)
|
||||
#define IM_DEBUG_BREAK() __debugbreak()
|
||||
#elif defined(__clang__)
|
||||
#define IM_DEBUG_BREAK() __builtin_debugtrap()
|
||||
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
|
||||
#define IM_DEBUG_BREAK() __asm__ volatile("int $0x03")
|
||||
#elif defined(__GNUC__) && defined(__thumb__)
|
||||
#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01")
|
||||
#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__)
|
||||
#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0");
|
||||
#else
|
||||
#define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger!
|
||||
#endif
|
||||
#endif // #ifndef IMGUI_DEBUG_BREAK
|
||||
|
||||
// [Options] We provide custom assert macro used by our our test suite, which you may use:
|
||||
// - Calling IM_DEBUG_BREAK() instead of an actual assert, so we can easily recover and step over (compared to many assert implementations).
|
||||
// - If a test is running, test name will be included in the log.
|
||||
// - Macro is calling IM_DEBUG_BREAK() inline to get debugger to break in the calling function (instead of a deeper callstack level).
|
||||
// - Macro is using comma operator instead of an if() to avoid "conditional expression is constant" warnings.
|
||||
extern void ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* func, int line);
|
||||
#define IM_TEST_ENGINE_ASSERT(_EXPR) do { if ((void)0, !(_EXPR)) { ImGuiTestEngine_AssertLog(#_EXPR, __FILE__, __func__, __LINE__); IM_DEBUG_BREAK(); } } while (0)
|
||||
// V_ASSERT_CONTRACT, assertMacro:IM_ASSERT
|
||||
213
libs/imgui_test_engine/imgui_te_internal.h
Normal file
213
libs/imgui_test_engine/imgui_te_internal.h
Normal file
@@ -0,0 +1,213 @@
|
||||
// dear imgui test engine
|
||||
// (internal api)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui_te_coroutine.h"
|
||||
#include "imgui_te_utils.h" // ImMovingAverage
|
||||
#include "imgui_capture_tool.h" // ImGuiCaptureTool // FIXME
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// FORWARD DECLARATIONS
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
class Str; // Str<> from thirdparty/Str/Str.h
|
||||
struct ImGuiPerfTool;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// DATA STRUCTURES
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Query item position/window/state given ID.
|
||||
struct ImGuiTestInfoTask
|
||||
{
|
||||
// Input
|
||||
ImGuiID ID = 0;
|
||||
int FrameCount = -1; // Timestamp of request
|
||||
char DebugName[64] = ""; // Debug string representing the queried ID
|
||||
|
||||
// Output
|
||||
ImGuiTestItemInfo Result;
|
||||
};
|
||||
|
||||
// Gather item list in given parent ID.
|
||||
struct ImGuiTestGatherTask
|
||||
{
|
||||
// Input
|
||||
ImGuiID InParentID = 0;
|
||||
int InMaxDepth = 0;
|
||||
short InLayerMask = 0;
|
||||
|
||||
// Output/Temp
|
||||
ImGuiTestItemList* OutList = NULL;
|
||||
ImGuiTestItemInfo* LastItemInfo = NULL;
|
||||
|
||||
void Clear() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// Find item ID given a label and a parent id
|
||||
// Usually used by queries with wildcards such as ItemInfo("hello/**/foo/bar")
|
||||
struct ImGuiTestFindByLabelTask
|
||||
{
|
||||
// Input
|
||||
ImGuiID InPrefixId = 0; // A known base ID which appears BEFORE the wildcard ID (for "hello/**/foo/bar" it would be hash of "hello")
|
||||
int InSuffixDepth = 0; // Number of labels in a path, after unknown base ID (for "hello/**/foo/bar" it would be 2)
|
||||
const char* InSuffix = NULL; // A label string which appears on ID stack after unknown base ID (for "hello/**/foo/bar" it would be "foo/bar")
|
||||
const char* InSuffixLastItem = NULL; // A last label string (for "hello/**/foo/bar" it would be "bar")
|
||||
ImGuiID InSuffixLastItemHash = 0;
|
||||
ImGuiItemStatusFlags InFilterItemStatusFlags = 0; // Flags required for item to be returned
|
||||
|
||||
// Output
|
||||
ImGuiID OutItemId = 0; // Result item ID
|
||||
};
|
||||
|
||||
enum ImGuiTestInputType
|
||||
{
|
||||
ImGuiTestInputType_None,
|
||||
ImGuiTestInputType_Key,
|
||||
ImGuiTestInputType_Char,
|
||||
ImGuiTestInputType_ViewportFocus,
|
||||
ImGuiTestInputType_ViewportClose
|
||||
};
|
||||
|
||||
// FIXME: May want to strip further now that core imgui is using its own input queue
|
||||
struct ImGuiTestInput
|
||||
{
|
||||
ImGuiTestInputType Type = ImGuiTestInputType_None;
|
||||
ImGuiKeyChord KeyChord = ImGuiKey_None;
|
||||
ImWchar Char = 0;
|
||||
bool Down = false;
|
||||
ImGuiID ViewportId = 0;
|
||||
|
||||
static ImGuiTestInput ForKeyChord(ImGuiKeyChord key_chord, bool down)
|
||||
{
|
||||
ImGuiTestInput inp;
|
||||
inp.Type = ImGuiTestInputType_Key;
|
||||
inp.KeyChord = key_chord;
|
||||
inp.Down = down;
|
||||
return inp;
|
||||
}
|
||||
|
||||
static ImGuiTestInput ForChar(ImWchar v)
|
||||
{
|
||||
ImGuiTestInput inp;
|
||||
inp.Type = ImGuiTestInputType_Char;
|
||||
inp.Char = v;
|
||||
return inp;
|
||||
}
|
||||
|
||||
static ImGuiTestInput ForViewportFocus(ImGuiID viewport_id)
|
||||
{
|
||||
ImGuiTestInput inp;
|
||||
inp.Type = ImGuiTestInputType_ViewportFocus;
|
||||
inp.ViewportId = viewport_id;
|
||||
return inp;
|
||||
}
|
||||
|
||||
static ImGuiTestInput ForViewportClose(ImGuiID viewport_id)
|
||||
{
|
||||
ImGuiTestInput inp;
|
||||
inp.Type = ImGuiTestInputType_ViewportClose;
|
||||
inp.ViewportId = viewport_id;
|
||||
return inp;
|
||||
}
|
||||
};
|
||||
|
||||
struct ImGuiTestInputs
|
||||
{
|
||||
ImVec2 MousePosValue; // Own non-rounded copy of MousePos in order facilitate simulating mouse movement very slow speed and high-framerate
|
||||
ImVec2 MouseWheel;
|
||||
ImGuiID MouseHoveredViewport = 0;
|
||||
int MouseButtonsValue = 0x00; // FIXME-TESTS: Use simulated_io.MouseDown[] ?
|
||||
ImVector<ImGuiTestInput> Queue;
|
||||
bool HostEscDown = false;
|
||||
float HostEscDownDuration = -1.0f; // Maintain our own DownDuration for host/backend ESC key so we can abort.
|
||||
};
|
||||
|
||||
// [Internal] Test Engine Context
|
||||
struct ImGuiTestEngine
|
||||
{
|
||||
ImGuiTestEngineIO IO;
|
||||
ImGuiContext* UiContextTarget = NULL; // imgui context for testing
|
||||
ImGuiContext* UiContextActive = NULL; // imgui context for testing == UiContextTarget or NULL
|
||||
|
||||
bool Started = false;
|
||||
ImU64 BatchStartTime = 0;
|
||||
ImU64 BatchEndTime = 0;
|
||||
int FrameCount = 0;
|
||||
float OverrideDeltaTime = -1.0f; // Inject custom delta time into imgui context to simulate clock passing faster than wall clock time.
|
||||
ImVector<ImGuiTest*> TestsAll;
|
||||
ImVector<ImGuiTestRunTask> TestsQueue;
|
||||
ImGuiTestContext* TestContext = NULL;
|
||||
ImVector<ImGuiTestInfoTask*>InfoTasks;
|
||||
ImGuiTestGatherTask GatherTask;
|
||||
ImGuiTestFindByLabelTask FindByLabelTask;
|
||||
ImGuiTestCoroutineHandle TestQueueCoroutine = NULL; // Coroutine to run the test queue
|
||||
bool TestQueueCoroutineShouldExit = false; // Flag to indicate that we are shutting down and the test queue coroutine should stop
|
||||
|
||||
// Inputs
|
||||
ImGuiTestInputs Inputs;
|
||||
|
||||
// UI support
|
||||
bool Abort = false;
|
||||
ImGuiTest* UiSelectAndScrollToTest = NULL;
|
||||
ImGuiTest* UiSelectedTest = NULL;
|
||||
Str* UiFilterTests;
|
||||
Str* UiFilterPerfs;
|
||||
ImU32 UiFilterByStatusMask = ~0u;
|
||||
bool UiMetricsOpen = false;
|
||||
bool UiDebugLogOpen = false;
|
||||
bool UiCaptureToolOpen = false;
|
||||
bool UiStackToolOpen = false;
|
||||
bool UiPerfToolOpen = false;
|
||||
float UiLogHeight = 150.0f;
|
||||
|
||||
// Performance Monitor
|
||||
double PerfRefDeltaTime;
|
||||
ImMovingAverage<double> PerfDeltaTime100;
|
||||
ImMovingAverage<double> PerfDeltaTime500;
|
||||
ImGuiPerfTool* PerfTool = NULL;
|
||||
|
||||
// Screen/Video Capturing
|
||||
ImGuiCaptureToolUI CaptureTool; // Capture tool UI
|
||||
ImGuiCaptureContext CaptureContext; // Capture context used in tests
|
||||
ImGuiCaptureArgs* CaptureCurrentArgs = NULL;
|
||||
|
||||
// Tools
|
||||
bool PostSwapCalled = false;
|
||||
bool ToolDebugRebootUiContext = false; // Completely shutdown and recreate the dear imgui context in place
|
||||
bool ToolSlowDown = false;
|
||||
int ToolSlowDownMs = 100;
|
||||
ImGuiTestRunSpeed BackupConfigRunSpeed = ImGuiTestRunSpeed_Fast;
|
||||
bool BackupConfigNoThrottle = false;
|
||||
|
||||
// Functions
|
||||
ImGuiTestEngine();
|
||||
~ImGuiTestEngine();
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// INTERNAL FUNCTIONS
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
ImGuiTestItemInfo* ImGuiTestEngine_FindItemInfo(ImGuiTestEngine* engine, ImGuiID id, const char* debug_id);
|
||||
void ImGuiTestEngine_Yield(ImGuiTestEngine* engine);
|
||||
void ImGuiTestEngine_SetDeltaTime(ImGuiTestEngine* engine, float delta_time);
|
||||
int ImGuiTestEngine_GetFrameCount(ImGuiTestEngine* engine);
|
||||
bool ImGuiTestEngine_PassFilter(ImGuiTest* test, const char* filter);
|
||||
void ImGuiTestEngine_RunTest(ImGuiTestEngine* engine, ImGuiTestContext* ctx, ImGuiTest* test, ImGuiTestRunFlags run_flags);
|
||||
|
||||
void ImGuiTestEngine_RebootUiContext(ImGuiTestEngine* engine);
|
||||
ImGuiPerfTool* ImGuiTestEngine_GetPerfTool(ImGuiTestEngine* engine);
|
||||
|
||||
// Screen/Video Capturing
|
||||
bool ImGuiTestEngine_CaptureScreenshot(ImGuiTestEngine* engine, ImGuiCaptureArgs* args);
|
||||
bool ImGuiTestEngine_CaptureBeginVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args);
|
||||
bool ImGuiTestEngine_CaptureEndVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args);
|
||||
|
||||
// Helper functions
|
||||
const char* ImGuiTestEngine_GetStatusName(ImGuiTestStatus v);
|
||||
const char* ImGuiTestEngine_GetRunSpeedName(ImGuiTestRunSpeed v);
|
||||
const char* ImGuiTestEngine_GetVerboseLevelName(ImGuiTestVerboseLevel v);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
1949
libs/imgui_test_engine/imgui_te_perftool.cpp
Normal file
1949
libs/imgui_test_engine/imgui_te_perftool.cpp
Normal file
File diff suppressed because it is too large
Load Diff
131
libs/imgui_test_engine/imgui_te_perftool.h
Normal file
131
libs/imgui_test_engine/imgui_te_perftool.h
Normal file
@@ -0,0 +1,131 @@
|
||||
// dear imgui test engine
|
||||
// (performance tool)
|
||||
// Browse and visualize samples recorded by ctx->PerfCapture() calls.
|
||||
// User access via 'Test Engine UI -> Tools -> Perf Tool'
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
// Forward Declaration
|
||||
struct ImGuiPerfToolColumnInfo;
|
||||
struct ImGuiTestEngine;
|
||||
struct ImGuiCsvParser;
|
||||
|
||||
// Configuration
|
||||
#define IMGUI_PERFLOG_DEFAULT_FILENAME "output/imgui_perflog.csv"
|
||||
|
||||
// [Internal] Perf log entry. Changes to this struct should be reflected in ImGuiTestContext::PerfCapture() and ImGuiTestEngine_Start().
|
||||
// This struct assumes strings stored here will be available until next ImGuiPerfTool::Clear() call. Fortunately we do not have to actively
|
||||
// manage lifetime of these strings. New entries are created only in two cases:
|
||||
// 1. ImGuiTestEngine_PerfToolAppendToCSV() call after perf test has run. This call receives ImGuiPerfToolEntry with const strings stored indefinitely by application.
|
||||
// 2. As a consequence of ImGuiPerfTool::LoadCSV() call, we persist the ImGuiCSVParser instance, which keeps parsed CSV text, from which strings are referenced.
|
||||
// As a result our solution also doesn't make many allocations.
|
||||
struct IMGUI_API ImGuiPerfToolEntry
|
||||
{
|
||||
ImU64 Timestamp = 0; // Title of a particular batch of perftool entries.
|
||||
const char* Category = NULL; // Name of category perf test is in.
|
||||
const char* TestName = NULL; // Name of perf test.
|
||||
double DtDeltaMs = 0.0; // Result of perf test.
|
||||
double DtDeltaMsMin = +FLT_MAX; // May be used by perftool.
|
||||
double DtDeltaMsMax = -FLT_MAX; // May be used by perftool.
|
||||
int NumSamples = 1; // Number aggregated samples.
|
||||
int PerfStressAmount = 0; //
|
||||
const char* GitBranchName = NULL; // Build information.
|
||||
const char* BuildType = NULL; //
|
||||
const char* Cpu = NULL; //
|
||||
const char* OS = NULL; //
|
||||
const char* Compiler = NULL; //
|
||||
const char* Date = NULL; // Date of this entry or min date of combined entries.
|
||||
//const char* DateMax = NULL; // Max date of combined entries, or NULL.
|
||||
double VsBaseline = 0.0; // Percent difference vs baseline.
|
||||
int LabelIndex = 0; // Index of TestName in ImGuiPerfTool::_LabelsVisible.
|
||||
|
||||
ImGuiPerfToolEntry() { }
|
||||
ImGuiPerfToolEntry(const ImGuiPerfToolEntry& rhs) { Set(rhs); }
|
||||
ImGuiPerfToolEntry& operator=(const ImGuiPerfToolEntry& rhs){ Set(rhs); return *this; }
|
||||
void Set(const ImGuiPerfToolEntry& rhs);
|
||||
};
|
||||
|
||||
// [Internal] Perf log batch.
|
||||
struct ImGuiPerfToolBatch
|
||||
{
|
||||
ImU64 BatchID = 0; // Timestamp of the batch, or unique ID of the build in combined mode.
|
||||
int NumSamples = 0; // A number of unique batches aggregated.
|
||||
int BranchIndex = 0; // For per-branch color mapping.
|
||||
ImVector<ImGuiPerfToolEntry> Entries; // Aggregated perf test entries. Order follows ImGuiPerfTool::_LabelsVisible order.
|
||||
~ImGuiPerfToolBatch() { Entries.clear_destruct(); } // FIXME: Misleading: nothing to destruct in that struct?
|
||||
};
|
||||
|
||||
enum ImGuiPerfToolDisplayType : int
|
||||
{
|
||||
ImGuiPerfToolDisplayType_Simple, // Each run will be displayed individually.
|
||||
ImGuiPerfToolDisplayType_PerBranchColors, // Use one bar color per branch.
|
||||
ImGuiPerfToolDisplayType_CombineByBuildInfo, // Entries with same build information will be averaged.
|
||||
};
|
||||
|
||||
//
|
||||
struct IMGUI_API ImGuiPerfTool
|
||||
{
|
||||
ImVector<ImGuiPerfToolEntry> _SrcData; // Raw entries from CSV file (with string pointer into CSV data).
|
||||
ImVector<const char*> _Labels;
|
||||
ImVector<const char*> _LabelsVisible; // ImPlot requires a pointer of all labels beforehand. Always contains a dummy "" entry at the end!
|
||||
ImVector<ImGuiPerfToolBatch> _Batches;
|
||||
ImGuiStorage _LabelBarCounts; // Number bars each label will render.
|
||||
int _NumVisibleBuilds = 0; // Cached number of visible builds.
|
||||
int _NumUniqueBuilds = 0; // Cached number of unique builds.
|
||||
ImGuiPerfToolDisplayType _DisplayType = ImGuiPerfToolDisplayType_CombineByBuildInfo;
|
||||
int _BaselineBatchIndex = 0; // Index of baseline build.
|
||||
ImU64 _BaselineTimestamp = 0;
|
||||
ImU64 _BaselineBuildId = 0;
|
||||
char _Filter[128]; // Context menu filtering substring.
|
||||
char _FilterDateFrom[11] = {};
|
||||
char _FilterDateTo[11] = {};
|
||||
float _InfoTableHeight = 180.0f;
|
||||
int _AlignStress = 0; // Alignment values for build info components, so they look aligned in the legend.
|
||||
int _AlignType = 0;
|
||||
int _AlignOs = 0;
|
||||
int _AlignCpu = 0;
|
||||
int _AlignCompiler = 0;
|
||||
int _AlignBranch = 0;
|
||||
int _AlignSamples = 0;
|
||||
bool _InfoTableSortDirty = false;
|
||||
ImVector<ImU64> _InfoTableSort; // _InfoTableSort[_LabelsVisible.Size * _Batches.Size]. Contains sorted batch indices for each label.
|
||||
const ImGuiTableSortSpecs* _InfoTableSortSpecs = NULL; // Current table sort specs.
|
||||
ImGuiStorage _TempSet; // Used as a set
|
||||
int _TableHoveredTest = -1; // Index within _VisibleLabelPointers array.
|
||||
int _TableHoveredBatch = -1;
|
||||
int _PlotHoverTest = -1;
|
||||
int _PlotHoverBatch = -1;
|
||||
bool _PlotHoverTestLabel = false;
|
||||
bool _ReportGenerating = false;
|
||||
ImGuiStorage _Visibility;
|
||||
ImGuiCsvParser* _CsvParser = NULL; // We keep this around and point to its fields
|
||||
|
||||
ImGuiPerfTool();
|
||||
~ImGuiPerfTool();
|
||||
|
||||
void Clear();
|
||||
bool LoadCSV(const char* filename = NULL);
|
||||
void AddEntry(ImGuiPerfToolEntry* entry);
|
||||
|
||||
void ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open);
|
||||
void ViewOnly(const char* perf_name);
|
||||
void ViewOnly(const char** perf_names);
|
||||
ImGuiPerfToolEntry* GetEntryByBatchIdx(int idx, const char* perf_name = NULL);
|
||||
bool SaveHtmlReport(const char* file_name, const char* image_file = NULL);
|
||||
inline bool Empty() { return _SrcData.empty(); }
|
||||
|
||||
void _Rebuild();
|
||||
bool _IsVisibleBuild(ImGuiPerfToolBatch* batch);
|
||||
bool _IsVisibleBuild(ImGuiPerfToolEntry* batch);
|
||||
bool _IsVisibleTest(const char* test_name);
|
||||
void _CalculateLegendAlignment();
|
||||
void _ShowEntriesPlot();
|
||||
void _ShowEntriesTable();
|
||||
void _SetBaseline(int batch_index);
|
||||
void _AddSettingsHandler();
|
||||
void _UnpackSortedKey(ImU64 key, int* batch_index, int* entry_index, int* monotonic_index = NULL);
|
||||
};
|
||||
|
||||
IMGUI_API void ImGuiTestEngine_PerfToolAppendToCSV(ImGuiPerfTool* perf_log, ImGuiPerfToolEntry* entry, const char* filename = NULL);
|
||||
842
libs/imgui_test_engine/imgui_te_ui.cpp
Normal file
842
libs/imgui_test_engine/imgui_te_ui.cpp
Normal file
@@ -0,0 +1,842 @@
|
||||
// dear imgui test engine
|
||||
// (ui)
|
||||
// If you run tests in an interactive or visible application, you may want to call ImGuiTestEngine_ShowTestEngineWindows()
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include "imgui_te_ui.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_te_engine.h"
|
||||
#include "imgui_te_context.h"
|
||||
#include "imgui_te_internal.h"
|
||||
#include "imgui_te_perftool.h"
|
||||
#include "thirdparty/Str/Str.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// TEST ENGINE: USER INTERFACE
|
||||
//-------------------------------------------------------------------------
|
||||
// - DrawTestLog() [internal]
|
||||
// - GetVerboseLevelName() [internal]
|
||||
// - ShowTestGroup() [internal]
|
||||
// - ImGuiTestEngine_ShowTestWindows()
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Look for " filename:number " in the string and add menu option to open source.
|
||||
static bool ParseLineAndDrawFileOpenItemForSourceFile(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end)
|
||||
{
|
||||
const char* separator = ImStrchrRange(line_start, line_end, ':');
|
||||
if (separator == NULL)
|
||||
return false;
|
||||
|
||||
const char* path_end = separator;
|
||||
const char* path_begin = separator - 1;
|
||||
while (path_begin > line_start&& path_begin[-1] != ' ')
|
||||
path_begin--;
|
||||
if (path_begin == path_end)
|
||||
return false;
|
||||
|
||||
int line_no = -1;
|
||||
sscanf(separator + 1, "%d ", &line_no);
|
||||
if (line_no == -1)
|
||||
return false;
|
||||
|
||||
Str256f buf("Open '%.*s' at line %d", (int)(path_end - path_begin), path_begin, line_no);
|
||||
if (ImGui::MenuItem(buf.c_str()))
|
||||
{
|
||||
// FIXME-TESTS: Assume folder is same as folder of test->SourceFile!
|
||||
const char* src_path = test->SourceFile;
|
||||
const char* src_name = ImPathFindFilename(src_path);
|
||||
buf.setf("%.*s%.*s", (int)(src_name - src_path), src_path, (int)(path_end - path_begin), path_begin);
|
||||
|
||||
ImGuiTestEngineIO& e_io = ImGuiTestEngine_GetIO(e);
|
||||
e_io.SrcFileOpenFunc(buf.c_str(), line_no, e_io.SrcFileOpenUserData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Look for "[ ,"]filename.png" in the string and add menu option to open image.
|
||||
static bool ParseLineAndDrawFileOpenItemForImageFile(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end, const char* file_ext)
|
||||
{
|
||||
IM_UNUSED(e);
|
||||
IM_UNUSED(test);
|
||||
|
||||
const char* extension = ImStristr(line_start, line_end, file_ext, NULL);
|
||||
if (extension == NULL)
|
||||
return false;
|
||||
|
||||
const char* path_end = extension + strlen(file_ext);
|
||||
const char* path_begin = extension - 1;
|
||||
while (path_begin > line_start && path_begin[-1] != ' ' && path_begin[-1] != '\'' && path_begin[-1] != '\"')
|
||||
path_begin--;
|
||||
if (path_begin == path_end)
|
||||
return false;
|
||||
|
||||
Str256 buf;
|
||||
|
||||
// Open file
|
||||
buf.setf("Open file: %.*s", (int)(path_end - path_begin), path_begin);
|
||||
if (ImGui::MenuItem(buf.c_str()))
|
||||
{
|
||||
buf.setf("%.*s", (int)(path_end - path_begin), path_begin);
|
||||
ImPathFixSeparatorsForCurrentOS(buf.c_str());
|
||||
ImOsOpenInShell(buf.c_str());
|
||||
}
|
||||
|
||||
// Open folder
|
||||
const char* folder_begin = path_begin;
|
||||
const char* folder_end = ImPathFindFilename(path_begin, path_end);
|
||||
buf.setf("Open folder: %.*s", (int)(folder_end - folder_begin), path_begin);
|
||||
if (ImGui::MenuItem(buf.c_str()))
|
||||
{
|
||||
buf.setf("%.*s", (int)(folder_end - folder_begin), folder_begin);
|
||||
ImPathFixSeparatorsForCurrentOS(buf.c_str());
|
||||
ImOsOpenInShell(buf.c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseLineAndDrawFileOpenItem(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end)
|
||||
{
|
||||
if (ParseLineAndDrawFileOpenItemForSourceFile(e, test, line_start, line_end))
|
||||
return true;
|
||||
if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, ".png"))
|
||||
return true;
|
||||
if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, ".gif"))
|
||||
return true;
|
||||
if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, ".mp4"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static float GetDpiScale()
|
||||
{
|
||||
#ifdef IMGUI_HAS_VIEWPORT
|
||||
return ImGui::GetWindowViewport()->DpiScale;
|
||||
#else
|
||||
return 1.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void DrawTestLog(ImGuiTestEngine* e, ImGuiTest* test)
|
||||
{
|
||||
const ImU32 error_col = IM_COL32(255, 150, 150, 255);
|
||||
const ImU32 warning_col = IM_COL32(240, 240, 150, 255);
|
||||
const ImU32 unimportant_col = IM_COL32(190, 190, 190, 255);
|
||||
const float dpi_scale = GetDpiScale();
|
||||
|
||||
ImGuiTestOutput* test_output = &test->Output;
|
||||
|
||||
ImGuiTestLog* log = &test_output->Log;
|
||||
const char* text = log->Buffer.begin();
|
||||
const char* text_end = log->Buffer.end();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 2.0f) * dpi_scale);
|
||||
ImGuiListClipper clipper;
|
||||
ImGuiTestVerboseLevel max_log_level = test_output->Status == ImGuiTestStatus_Error ? e->IO.ConfigVerboseLevelOnError : e->IO.ConfigVerboseLevel;
|
||||
int line_count = log->ExtractLinesForVerboseLevels(ImGuiTestVerboseLevel_Silent, max_log_level, NULL);
|
||||
int current_index_clipped = -1;
|
||||
int current_index_abs = 0;
|
||||
clipper.Begin(line_count);
|
||||
while (clipper.Step())
|
||||
{
|
||||
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
|
||||
{
|
||||
// Advance index_by_log_level to find log entry indicated by line_no.
|
||||
ImGuiTestLogLineInfo* line_info = NULL;
|
||||
while (current_index_clipped < line_no)
|
||||
{
|
||||
line_info = &log->LineInfo[current_index_abs];
|
||||
if (line_info->Level <= max_log_level)
|
||||
current_index_clipped++;
|
||||
current_index_abs++;
|
||||
}
|
||||
|
||||
const char* line_start = text + line_info->LineOffset;
|
||||
const char* line_end = strchr(line_start, '\n');
|
||||
if (line_end == NULL)
|
||||
line_end = text_end;
|
||||
|
||||
switch (line_info->Level)
|
||||
{
|
||||
case ImGuiTestVerboseLevel_Error:
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, error_col);
|
||||
break;
|
||||
case ImGuiTestVerboseLevel_Warning:
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, warning_col);
|
||||
break;
|
||||
case ImGuiTestVerboseLevel_Debug:
|
||||
case ImGuiTestVerboseLevel_Trace:
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, unimportant_col);
|
||||
break;
|
||||
default:
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32_WHITE);
|
||||
break;
|
||||
}
|
||||
ImGui::TextUnformatted(line_start, line_end);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::PushID(line_no);
|
||||
if (ImGui::BeginPopupContextItem("Context", 1))
|
||||
{
|
||||
if (!ParseLineAndDrawFileOpenItem(e, test, line_start, line_end))
|
||||
ImGui::MenuItem("No options", NULL, false, false);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
#if IMGUI_VERSION_NUM <= 18963
|
||||
namespace ImGui
|
||||
{
|
||||
void SetItemTooltip(const char* fmt, ...)
|
||||
{
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
ImGui::SetTooltipV(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
} // namespace ImGui
|
||||
#endif
|
||||
|
||||
static bool ShowTestGroupFilterTest(ImGuiTestEngine* e, ImGuiTestGroup group, const char* filter, ImGuiTest* test)
|
||||
{
|
||||
if (test->Group != group)
|
||||
return false;
|
||||
if (!ImGuiTestEngine_PassFilter(test, *filter ? filter : "all"))
|
||||
return false;
|
||||
if ((e->UiFilterByStatusMask & (1 << test->Output.Status)) == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void GetFailingTestsAsString(ImGuiTestEngine* e, ImGuiTestGroup group, char separator, Str* out_string)
|
||||
{
|
||||
IM_ASSERT(out_string != NULL);
|
||||
bool first = true;
|
||||
for (int i = 0; i < e->TestsAll.Size; i++)
|
||||
{
|
||||
ImGuiTest* failing_test = e->TestsAll[i];
|
||||
Str* filter = (group == ImGuiTestGroup_Tests) ? e->UiFilterTests : e->UiFilterPerfs;
|
||||
if (failing_test->Group != group)
|
||||
continue;
|
||||
if (failing_test->Output.Status != ImGuiTestStatus_Error)
|
||||
continue;
|
||||
if (!ImGuiTestEngine_PassFilter(failing_test, filter->empty() ? "all" : filter->c_str()))
|
||||
continue;
|
||||
if (!first)
|
||||
out_string->append(separator);
|
||||
out_string->append(failing_test->Name);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void TestStatusButton(const char* id, const ImVec4& color, bool running, int display_counter)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true);
|
||||
ImGui::ColorButton(id, color, ImGuiColorEditFlags_NoTooltip);
|
||||
ImGui::PopItemFlag();
|
||||
if (running)
|
||||
{
|
||||
//ImRect r = g.LastItemData.Rect;
|
||||
ImVec2 center = g.LastItemData.Rect.GetCenter();
|
||||
float radius = ImFloor(ImMin(g.LastItemData.Rect.GetWidth(), g.LastItemData.Rect.GetHeight()) * 0.40f);
|
||||
float t = (float)(ImGui::GetTime() * 20.0f);
|
||||
ImVec2 off(ImCos(t) * radius, ImSin(t) * radius);
|
||||
ImGui::GetWindowDrawList()->AddLine(center - off, center + off, ImGui::GetColorU32(ImGuiCol_Text), 1.5f);
|
||||
//ImGui::RenderText(r.Min + style.FramePadding + ImVec2(0, 0), &"|\0/\0-\0\\"[(((ImGui::GetFrameCount() / 5) & 3) << 1)], NULL);
|
||||
}
|
||||
else if (display_counter >= 0)
|
||||
{
|
||||
ImVec2 center = g.LastItemData.Rect.GetCenter();
|
||||
Str30f buf("%d", display_counter);
|
||||
ImGui::GetWindowDrawList()->AddText(center - ImGui::CalcTextSize(buf.c_str()) * 0.5f, ImGui::GetColorU32(ImGuiCol_Text), buf.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter)
|
||||
{
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
const float dpi_scale = GetDpiScale();
|
||||
|
||||
// Colored Status button: will be displayed later below
|
||||
// - Save position of test run status button and make space for it.
|
||||
const ImVec2 status_button_pos = ImGui::GetCursorPos();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetFrameHeight() + style.ItemInnerSpacing.x);
|
||||
|
||||
//ImGui::Text("TESTS (%d)", engine->TestsAll.Size);
|
||||
#if IMGUI_VERSION_NUM >= 18837
|
||||
bool run = ImGui::Button("Run") || ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_R);
|
||||
#else
|
||||
bool = ImGui::Button("Run");
|
||||
#endif
|
||||
#if IMGUI_VERSION_NUM > 18963
|
||||
ImGui::SetItemTooltip("Ctrl+R");
|
||||
#endif
|
||||
if (run)
|
||||
{
|
||||
for (int n = 0; n < e->TestsAll.Size; n++)
|
||||
{
|
||||
ImGuiTest* test = e->TestsAll[n];
|
||||
if (!ShowTestGroupFilterTest(e, group, filter->c_str(), test))
|
||||
continue;
|
||||
ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_None);
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
{
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 6.0f);
|
||||
const char* filter_by_status_desc = "";
|
||||
if (e->UiFilterByStatusMask == ~0u)
|
||||
filter_by_status_desc = "All";
|
||||
else if (e->UiFilterByStatusMask == ~(1u << ImGuiTestStatus_Success))
|
||||
filter_by_status_desc = "Not OK";
|
||||
else if (e->UiFilterByStatusMask == (1u << ImGuiTestStatus_Error))
|
||||
filter_by_status_desc = "Errors";
|
||||
if (ImGui::BeginCombo("##filterbystatus", filter_by_status_desc))
|
||||
{
|
||||
if (ImGui::Selectable("All", e->UiFilterByStatusMask == ~0u))
|
||||
e->UiFilterByStatusMask = (ImU32)~0u;
|
||||
if (ImGui::Selectable("Not OK", e->UiFilterByStatusMask == ~(1u << ImGuiTestStatus_Success)))
|
||||
e->UiFilterByStatusMask = (ImU32)~(1u << ImGuiTestStatus_Success);
|
||||
if (ImGui::Selectable("Errors", e->UiFilterByStatusMask == (1u << ImGuiTestStatus_Error)))
|
||||
e->UiFilterByStatusMask = (ImU32)(1u << ImGuiTestStatus_Error);
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
const char* perflog_label = "Perf Tool";
|
||||
float filter_width = ImGui::GetWindowContentRegionMax().x - ImGui::GetCursorPos().x;
|
||||
float perf_stress_factor_width = (30 * dpi_scale);
|
||||
if (group == ImGuiTestGroup_Perfs)
|
||||
{
|
||||
filter_width -= style.ItemSpacing.x + perf_stress_factor_width;
|
||||
filter_width -= style.ItemSpacing.x + style.FramePadding.x * 2 + ImGui::CalcTextSize(perflog_label).x;
|
||||
}
|
||||
filter_width -= ImGui::CalcTextSize("(?)").x + style.ItemSpacing.x;
|
||||
ImGui::SetNextItemWidth(ImMax(20.0f, filter_width));
|
||||
ImGui::InputText("##filter", filter);
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("(?)");
|
||||
ImGui::SetItemTooltip("Query is composed of one or more comma-separated filter terms with optional modifiers.\n"
|
||||
"Available modifiers:\n"
|
||||
"- '-' prefix excludes tests matched by the term.\n"
|
||||
"- '^' prefix anchors term matching to the start of the string.\n"
|
||||
"- '$' suffix anchors term matching to the end of the string.");
|
||||
if (group == ImGuiTestGroup_Perfs)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(perf_stress_factor_width);
|
||||
ImGui::DragInt("##PerfStress", &e->IO.PerfStressAmount, 0.1f, 1, 20, "x%d");
|
||||
ImGui::SetItemTooltip("Increase workload of performance tests (higher means longer run)."); // FIXME: Move?
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(perflog_label))
|
||||
{
|
||||
e->UiPerfToolOpen = true;
|
||||
ImGui::FocusWindow(ImGui::FindWindowByName("Dear ImGui Perf Tool"));
|
||||
}
|
||||
}
|
||||
|
||||
int tests_completed = 0;
|
||||
int tests_succeeded = 0;
|
||||
int tests_failed = 0;
|
||||
if (ImGui::BeginTable("Tests", 3, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_SizingFixedFit))
|
||||
{
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("Status");
|
||||
ImGui::TableSetupColumn("Category");
|
||||
ImGui::TableSetupColumn("Test", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6, 4) * dpi_scale);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 0) * dpi_scale);
|
||||
//ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(100, 10) * dpi_scale);
|
||||
for (int test_n = 0; test_n < e->TestsAll.Size; test_n++)
|
||||
{
|
||||
ImGuiTest* test = e->TestsAll[test_n];
|
||||
if (!ShowTestGroupFilterTest(e, group, filter->c_str(), test))
|
||||
continue;
|
||||
|
||||
ImGuiTestOutput* test_output = &test->Output;
|
||||
ImGuiTestContext* test_context = (e->TestContext && e->TestContext->Test == test) ? e->TestContext : NULL; // Running context, if any
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(test_n);
|
||||
|
||||
// Colors match general test status colors defined below.
|
||||
ImVec4 status_color;
|
||||
switch (test_output->Status)
|
||||
{
|
||||
case ImGuiTestStatus_Error:
|
||||
status_color = ImVec4(0.9f, 0.1f, 0.1f, 1.0f);
|
||||
tests_completed++;
|
||||
tests_failed++;
|
||||
break;
|
||||
case ImGuiTestStatus_Success:
|
||||
status_color = ImVec4(0.1f, 0.9f, 0.1f, 1.0f);
|
||||
tests_completed++;
|
||||
tests_succeeded++;
|
||||
break;
|
||||
case ImGuiTestStatus_Queued:
|
||||
case ImGuiTestStatus_Running:
|
||||
case ImGuiTestStatus_Suspended:
|
||||
if (test_context && (test_context->RunFlags & ImGuiTestRunFlags_GuiFuncOnly))
|
||||
status_color = ImVec4(0.8f, 0.0f, 0.8f, 1.0f);
|
||||
else
|
||||
status_color = ImVec4(0.8f, 0.4f, 0.1f, 1.0f);
|
||||
break;
|
||||
default:
|
||||
status_color = ImVec4(0.4f, 0.4f, 0.4f, 1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
TestStatusButton("status", status_color, test_output->Status == ImGuiTestStatus_Running || test_output->Status == ImGuiTestStatus_Suspended, -1);
|
||||
ImGui::SameLine();
|
||||
|
||||
bool queue_test = false;
|
||||
bool queue_gui_func_toggle = false;
|
||||
bool select_test = false;
|
||||
|
||||
if (test_output->Status == ImGuiTestStatus_Suspended)
|
||||
{
|
||||
// Resume IM_SUSPEND_TESTFUNC
|
||||
// FIXME: Terrible user experience to have this here.
|
||||
if (ImGui::Button("Con###Run"))
|
||||
test_output->Status = ImGuiTestStatus_Running;
|
||||
ImGui::SetItemTooltip("CTRL+Space to continue.");
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Space) && io.KeyCtrl)
|
||||
test_output->Status = ImGuiTestStatus_Running;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGui::Button("Run###Run"))
|
||||
queue_test = select_test = true;
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(test->Category, test == e->UiSelectedTest, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnNav))
|
||||
select_test = true;
|
||||
|
||||
// Double-click to run test, CTRL+Double-click to run GUI function
|
||||
const bool is_running_gui_func = (test_context && (test_context->RunFlags & ImGuiTestRunFlags_GuiFuncOnly));
|
||||
const bool has_gui_func = (test->GuiFunc != NULL);
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0))
|
||||
{
|
||||
if (ImGui::GetIO().KeyCtrl)
|
||||
queue_gui_func_toggle = true;
|
||||
else
|
||||
queue_test = true;
|
||||
}
|
||||
|
||||
/*if (ImGui::IsItemHovered() && test->TestLog.size() > 0)
|
||||
{
|
||||
ImGui::BeginTooltip();
|
||||
DrawTestLog(engine, test, false);
|
||||
ImGui::EndTooltip();
|
||||
}*/
|
||||
|
||||
if (e->UiSelectAndScrollToTest == test)
|
||||
ImGui::SetScrollHereY();
|
||||
|
||||
bool view_source = false;
|
||||
if (ImGui::BeginPopupContextItem())
|
||||
{
|
||||
select_test = true;
|
||||
|
||||
if (ImGui::MenuItem("Run test"))
|
||||
queue_test = true;
|
||||
if (ImGui::MenuItem("Run GUI func", "Ctrl+DblClick", is_running_gui_func, has_gui_func))
|
||||
queue_gui_func_toggle = true;
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
const bool open_source_available = (test->SourceFile != NULL) && (e->IO.SrcFileOpenFunc != NULL);
|
||||
|
||||
Str128 buf;
|
||||
if (test->SourceFile != NULL) // This is normally set by IM_REGISTER_TEST() but custom registration may omit it.
|
||||
buf.setf("Open source (%s:%d)", ImPathFindFilename(test->SourceFile), test->SourceLine);
|
||||
else
|
||||
buf.set("Open source");
|
||||
if (ImGui::MenuItem(buf.c_str(), NULL, false, open_source_available))
|
||||
e->IO.SrcFileOpenFunc(test->SourceFile, test->SourceLine, e->IO.SrcFileOpenUserData);
|
||||
if (ImGui::MenuItem("View source...", NULL, false, test->SourceFile != NULL))
|
||||
view_source = true;
|
||||
|
||||
if (group == ImGuiTestGroup_Perfs && ImGui::MenuItem("View perflog"))
|
||||
{
|
||||
e->PerfTool->ViewOnly(test->Name);
|
||||
e->UiPerfToolOpen = true;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Copy name", NULL, false))
|
||||
ImGui::SetClipboardText(test->Name);
|
||||
|
||||
if (test_output->Status == ImGuiTestStatus_Error)
|
||||
if (ImGui::MenuItem("Copy names of all failing tests"))
|
||||
{
|
||||
Str256 failing_tests;
|
||||
GetFailingTestsAsString(e, group, ',', &failing_tests);
|
||||
ImGui::SetClipboardText(failing_tests.c_str());
|
||||
}
|
||||
|
||||
ImGuiTestLog* test_log = &test_output->Log;
|
||||
if (ImGui::BeginMenu("Copy log", !test_log->IsEmpty()))
|
||||
{
|
||||
for (int level_n = ImGuiTestVerboseLevel_Error; level_n < ImGuiTestVerboseLevel_COUNT; level_n++)
|
||||
{
|
||||
ImGuiTestVerboseLevel level = (ImGuiTestVerboseLevel)level_n;
|
||||
int count = test_log->ExtractLinesForVerboseLevels((ImGuiTestVerboseLevel)0, level, NULL);
|
||||
if (ImGui::MenuItem(Str64f("%s (%d lines)", ImGuiTestEngine_GetVerboseLevelName(level), count).c_str(), NULL, false, count > 0))
|
||||
{
|
||||
ImGuiTextBuffer buffer;
|
||||
test_log->ExtractLinesForVerboseLevels((ImGuiTestVerboseLevel)0, level, &buffer);
|
||||
ImGui::SetClipboardText(buffer.c_str());
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Clear log", NULL, false, !test_log->IsEmpty()))
|
||||
test_log->Clear();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// Process source popup
|
||||
static ImGuiTextBuffer source_blurb;
|
||||
static int goto_line = -1;
|
||||
if (view_source)
|
||||
{
|
||||
source_blurb.clear();
|
||||
size_t file_size = 0;
|
||||
char* file_data = (char*)ImFileLoadToMemory(test->SourceFile, "rb", &file_size);
|
||||
if (file_data)
|
||||
source_blurb.append(file_data, file_data + file_size);
|
||||
else
|
||||
source_blurb.append("<Error loading sources>");
|
||||
goto_line = (test->SourceLine + test->SourceLineEnd) / 2;
|
||||
ImGui::OpenPopup("Source");
|
||||
}
|
||||
if (ImGui::BeginPopup("Source"))
|
||||
{
|
||||
// FIXME: Local vs screen pos too messy :(
|
||||
const ImVec2 start_pos = ImGui::GetCursorStartPos();
|
||||
const float line_height = ImGui::GetTextLineHeight();
|
||||
if (goto_line != -1)
|
||||
ImGui::SetScrollFromPosY(start_pos.y + (goto_line - 1) * line_height, 0.5f);
|
||||
goto_line = -1;
|
||||
|
||||
ImRect r(0.0f, test->SourceLine * line_height, ImGui::GetWindowWidth(), (test->SourceLine + 1) * line_height); // SourceLineEnd is too flaky
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetWindowPos() + start_pos + r.Min, ImGui::GetWindowPos() + start_pos + r.Max, IM_COL32(80, 80, 150, 150));
|
||||
|
||||
ImGui::TextUnformatted(source_blurb.c_str(), source_blurb.end());
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(test->Name);
|
||||
|
||||
// Process selection
|
||||
if (select_test)
|
||||
e->UiSelectedTest = test;
|
||||
|
||||
// Process queuing
|
||||
if (queue_gui_func_toggle && is_running_gui_func)
|
||||
ImGuiTestEngine_AbortCurrentTest(e);
|
||||
else if (queue_gui_func_toggle && !e->IO.IsRunningTests)
|
||||
ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_RunFromGui | ImGuiTestRunFlags_GuiFuncOnly);
|
||||
if (queue_test && !e->IO.IsRunningTests)
|
||||
ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_RunFromGui);
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// Display test status recap (colors match per-test run button colors defined above)
|
||||
{
|
||||
ImVec4 status_color;
|
||||
if (tests_failed > 0)
|
||||
status_color = ImVec4(0.9f, 0.1f, 0.1f, 1.0f); // Red
|
||||
else if (e->IO.IsRunningTests)
|
||||
status_color = ImVec4(0.8f, 0.4f, 0.1f, 1.0f);
|
||||
else if (tests_succeeded > 0 && tests_completed == tests_succeeded)
|
||||
status_color = ImVec4(0.1f, 0.9f, 0.1f, 1.0f);
|
||||
else
|
||||
status_color = ImVec4(0.4f, 0.4f, 0.4f, 1.0f);
|
||||
//ImVec2 cursor_pos_bkp = ImGui::GetCursorPos();
|
||||
ImGui::SetCursorPos(status_button_pos);
|
||||
TestStatusButton("status", status_color, false, tests_failed > 0 ? tests_failed : -1);// e->IO.IsRunningTests);
|
||||
ImGui::SetItemTooltip("Filtered: %d\n- OK: %d\n- Errors: %d", tests_completed, tests_succeeded, tests_failed);
|
||||
//ImGui::SetCursorPos(cursor_pos_bkp); // Restore cursor position for rendering further widgets
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGuiTestEngine_ShowLogAndTools(ImGuiTestEngine* engine)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
const float dpi_scale = GetDpiScale();
|
||||
|
||||
if (!ImGui::BeginTabBar("##tools"))
|
||||
return;
|
||||
|
||||
if (ImGui::BeginTabItem("LOG"))
|
||||
{
|
||||
ImGuiTest* selected_test = engine->UiSelectedTest;
|
||||
|
||||
if (selected_test != NULL)
|
||||
ImGui::Text("Log for '%s' '%s'", selected_test->Category, selected_test->Name);
|
||||
else
|
||||
ImGui::Text("N/A");
|
||||
if (ImGui::SmallButton("Clear"))
|
||||
if (selected_test)
|
||||
selected_test->Output.Log.Clear();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Copy to clipboard"))
|
||||
if (engine->UiSelectedTest)
|
||||
ImGui::SetClipboardText(selected_test->Output.Log.Buffer.c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::BeginChild("Log");
|
||||
if (engine->UiSelectedTest)
|
||||
{
|
||||
DrawTestLog(engine, engine->UiSelectedTest);
|
||||
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
|
||||
ImGui::SetScrollHereY();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
// Options
|
||||
if (ImGui::BeginTabItem("OPTIONS"))
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
ImGui::Text("TestEngine: HookItems: %d, HookPushId: %d, InfoTasks: %d", g.TestEngineHookItems, g.DebugHookIdInfo != 0, engine->InfoTasks.Size);
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("Reboot UI context"))
|
||||
engine->ToolDebugRebootUiContext = true;
|
||||
|
||||
const ImGuiInputTextCallback filter_callback = [](ImGuiInputTextCallbackData* data) { return (data->EventChar == ',' || data->EventChar == ';') ? 1 : 0; };
|
||||
ImGui::InputText("Branch/Annotation", engine->IO.GitBranchName, IM_ARRAYSIZE(engine->IO.GitBranchName), ImGuiInputTextFlags_CallbackCharFilter, filter_callback, NULL);
|
||||
ImGui::SetItemTooltip("This will be stored in the CSV file for performance tools.");
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::TreeNode("Screen/video capture"))
|
||||
{
|
||||
ImGui::Checkbox("Capture when requested by API", &engine->IO.ConfigCaptureEnabled);
|
||||
ImGui::SetItemTooltip("Enable or disable screen capture API completely.");
|
||||
ImGui::Checkbox("Capture screen on error", &engine->IO.ConfigCaptureOnError);
|
||||
ImGui::SetItemTooltip("Capture a screenshot on test failure.");
|
||||
|
||||
// Fields modified by in this call will be synced to engine->CaptureContext.
|
||||
engine->CaptureTool._ShowEncoderConfigFields(&engine->CaptureContext);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Performances"))
|
||||
{
|
||||
ImGui::Checkbox("Slow down whole app", &engine->ToolSlowDown);
|
||||
ImGui::SameLine(); ImGui::SetNextItemWidth(70 * dpi_scale);
|
||||
ImGui::SliderInt("##ms", &engine->ToolSlowDownMs, 0, 400, "%d ms");
|
||||
|
||||
// FIXME-TESTS: Need to be visualizing the samples/spikes.
|
||||
double dt_1 = 1.0 / ImGui::GetIO().Framerate;
|
||||
double fps_now = 1.0 / dt_1;
|
||||
double dt_100 = engine->PerfDeltaTime100.GetAverage();
|
||||
double dt_500 = engine->PerfDeltaTime500.GetAverage();
|
||||
|
||||
//if (engine->PerfRefDeltaTime <= 0.0 && engine->PerfRefDeltaTime.IsFull())
|
||||
// engine->PerfRefDeltaTime = dt_2000;
|
||||
|
||||
ImGui::Checkbox("Unthrolled", &engine->IO.ConfigNoThrottle);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Pick ref dt"))
|
||||
engine->PerfRefDeltaTime = dt_500;
|
||||
|
||||
double dt_ref = engine->PerfRefDeltaTime;
|
||||
ImGui::Text("[ref dt] %6.3f ms", engine->PerfRefDeltaTime * 1000);
|
||||
ImGui::Text("[last 001] %6.3f ms (%.1f FPS) ++ %6.3f ms", dt_1 * 1000.0, 1.0 / dt_1, (dt_1 - dt_ref) * 1000);
|
||||
ImGui::Text("[last 100] %6.3f ms (%.1f FPS) ++ %6.3f ms ~ converging in %.1f secs", dt_100 * 1000.0, 1.0 / dt_100, (dt_1 - dt_ref) * 1000, 100.0 / fps_now);
|
||||
ImGui::Text("[last 500] %6.3f ms (%.1f FPS) ++ %6.3f ms ~ converging in %.1f secs", dt_500 * 1000.0, 1.0 / dt_500, (dt_1 - dt_ref) * 1000, 500.0 / fps_now);
|
||||
|
||||
//ImGui::PlotLines("Last 100", &engine->PerfDeltaTime100.Samples.Data, engine->PerfDeltaTime100.Samples.Size, engine->PerfDeltaTime100.Idx, NULL, 0.0f, dt_1000 * 1.10f, ImVec2(0.0f, ImGui::GetFontSize()));
|
||||
ImVec2 plot_size(0.0f, ImGui::GetFrameHeight() * 3);
|
||||
ImMovingAverage<double>* ma = &engine->PerfDeltaTime500;
|
||||
ImGui::PlotLines("Last 500",
|
||||
[](void* data, int n) { ImMovingAverage<double>* ma = (ImMovingAverage<double>*)data; return (float)(ma->Samples[n] * 1000); },
|
||||
ma, ma->Samples.Size, 0 * ma->Idx, NULL, 0.0f, (float)(ImMax(dt_100, dt_500) * 1000.0 * 1.2f), plot_size);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Dear ImGui Configuration Flags"))
|
||||
{
|
||||
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);
|
||||
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);
|
||||
#ifdef IMGUI_HAS_DOCK
|
||||
ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar);
|
||||
#endif
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
static void ImGuiTestEngine_ShowTestTool(ImGuiTestEngine* engine, bool* p_open)
|
||||
{
|
||||
const float dpi_scale = GetDpiScale();
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(ImGui::GetFontSize() * 50, ImGui::GetFontSize() * 40), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin("Dear ImGui Test Engine", p_open, ImGuiWindowFlags_MenuBar))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenuBar())
|
||||
{
|
||||
if (ImGui::BeginMenu("Tools"))
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGui::MenuItem("Metrics/Debugger", "", &engine->UiMetricsOpen);
|
||||
ImGui::MenuItem("Debug Log", "", &engine->UiDebugLogOpen);
|
||||
ImGui::MenuItem("Stack Tool", "", &engine->UiStackToolOpen);
|
||||
ImGui::MenuItem("Item Picker", "", &g.DebugItemPickerActive);
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("Capture Tool", "", &engine->UiCaptureToolOpen);
|
||||
ImGui::MenuItem("Perf Tool", "", &engine->UiPerfToolOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(90 * dpi_scale);
|
||||
if (ImGui::BeginCombo("##RunSpeed", ImGuiTestEngine_GetRunSpeedName(engine->IO.ConfigRunSpeed), ImGuiComboFlags_None))
|
||||
{
|
||||
for (ImGuiTestRunSpeed level = (ImGuiTestRunSpeed)0; level < ImGuiTestRunSpeed_COUNT; level = (ImGuiTestRunSpeed)(level + 1))
|
||||
if (ImGui::Selectable(ImGuiTestEngine_GetRunSpeedName(level), engine->IO.ConfigRunSpeed == level))
|
||||
engine->IO.ConfigRunSpeed = level;
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SetItemTooltip(
|
||||
"Running speed\n"
|
||||
"- Fast: Run tests as fast as possible (no delay/vsync, teleport mouse, etc.).\n"
|
||||
"- Normal: Run tests at human watchable speed (for debugging).\n"
|
||||
"- Cinematic: Run tests with pauses between actions (for e.g. tutorials)."
|
||||
);
|
||||
ImGui::SameLine();
|
||||
//ImGui::Checkbox("Fast", &engine->IO.ConfigRunFast);
|
||||
//ImGui::SameLine();
|
||||
ImGui::Checkbox("Stop", &engine->IO.ConfigStopOnError);
|
||||
ImGui::SetItemTooltip("Stop running tests when hitting an error.");
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("DbgBrk", &engine->IO.ConfigBreakOnError);
|
||||
ImGui::SetItemTooltip("Break in debugger when hitting an error.");
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("KeepGUI", &engine->IO.ConfigKeepGuiFunc);
|
||||
ImGui::SetItemTooltip("Keep GUI function running after a test fails, or when a single queued test is finished.\nHold ESC to abort a running GUI function.");
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Refocus", &engine->IO.ConfigRestoreFocusAfterTests);
|
||||
ImGui::SetItemTooltip("Restore focus back after running tests.");
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(70 * dpi_scale);
|
||||
if (ImGui::BeginCombo("##Verbose", ImGuiTestEngine_GetVerboseLevelName(engine->IO.ConfigVerboseLevel), ImGuiComboFlags_None))
|
||||
{
|
||||
for (ImGuiTestVerboseLevel level = (ImGuiTestVerboseLevel)0; level < ImGuiTestVerboseLevel_COUNT; level = (ImGuiTestVerboseLevel)(level + 1))
|
||||
if (ImGui::Selectable(ImGuiTestEngine_GetVerboseLevelName(level), engine->IO.ConfigVerboseLevel == level))
|
||||
engine->IO.ConfigVerboseLevel = engine->IO.ConfigVerboseLevelOnError = level;
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SetItemTooltip("Verbose level.");
|
||||
//ImGui::PopStyleVar();
|
||||
ImGui::Separator();
|
||||
|
||||
// SPLITTER
|
||||
// FIXME-OPT: A better splitter API supporting arbitrary number of splits would be useful.
|
||||
float list_height = 0.0f;
|
||||
float& log_height = engine->UiLogHeight;
|
||||
ImGui::Splitter("splitter", &list_height, &log_height, ImGuiAxis_Y, +1);
|
||||
|
||||
// TESTS
|
||||
ImGui::BeginChild("List", ImVec2(0, list_height), false, ImGuiWindowFlags_NoScrollbar);
|
||||
if (ImGui::BeginTabBar("##Tests", ImGuiTabBarFlags_NoTooltip)) // Add _NoPushId flag in TabBar?
|
||||
{
|
||||
if (ImGui::BeginTabItem("TESTS", NULL, ImGuiTabItemFlags_NoPushId))
|
||||
{
|
||||
ShowTestGroup(engine, ImGuiTestGroup_Tests, engine->UiFilterTests);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("PERFS", NULL, ImGuiTabItemFlags_NoPushId))
|
||||
{
|
||||
ShowTestGroup(engine, ImGuiTestGroup_Perfs, engine->UiFilterPerfs);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
engine->UiSelectAndScrollToTest = NULL;
|
||||
|
||||
// LOG & TOOLS
|
||||
ImGui::BeginChild("Log", ImVec2(0, log_height));
|
||||
ImGuiTestEngine_ShowLogAndTools(engine);
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ImGuiTestEngine_ShowTestEngineWindows(ImGuiTestEngine* e, bool* p_open)
|
||||
{
|
||||
// Test Tool
|
||||
ImGuiTestEngine_ShowTestTool(e, p_open);
|
||||
|
||||
// Stack Tool
|
||||
#if IMGUI_VERSION_NUM < 18993
|
||||
if (e->UiStackToolOpen)
|
||||
ImGui::ShowStackToolWindow(&e->UiStackToolOpen);
|
||||
#else
|
||||
if (e->UiStackToolOpen)
|
||||
ImGui::ShowIDStackToolWindow(&e->UiStackToolOpen);
|
||||
#endif
|
||||
|
||||
// Capture Tool
|
||||
if (e->UiCaptureToolOpen)
|
||||
e->CaptureTool.ShowCaptureToolWindow(&e->CaptureContext, &e->UiCaptureToolOpen);
|
||||
|
||||
// Performance tool
|
||||
if (e->UiPerfToolOpen)
|
||||
e->PerfTool->ShowPerfToolWindow(e, &e->UiPerfToolOpen);;
|
||||
|
||||
// Show Dear ImGui windows
|
||||
// (we cannot show demo window here because it could lead to duplicate display, which demo windows isn't guarded for)
|
||||
if (e->UiMetricsOpen)
|
||||
ImGui::ShowMetricsWindow(&e->UiMetricsOpen);
|
||||
if (e->UiDebugLogOpen)
|
||||
ImGui::ShowDebugLogWindow(&e->UiDebugLogOpen);
|
||||
}
|
||||
21
libs/imgui_test_engine/imgui_te_ui.h
Normal file
21
libs/imgui_test_engine/imgui_te_ui.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// dear imgui test engine
|
||||
// (ui)
|
||||
// If you run tests in an interactive or visible application, you may want to call ImGuiTestEngine_ShowTestEngineWindows()
|
||||
|
||||
// Provide access to:
|
||||
// - "Dear ImGui Test Engine" main interface
|
||||
// - "Dear ImGui Capture Tool"
|
||||
// - "Dear ImGui Perf Tool"
|
||||
// - other core debug functions: Metrics, Debug Log
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef IMGUI_VERSION
|
||||
#include "imgui.h" // IMGUI_API
|
||||
#endif
|
||||
|
||||
// Forward declarations
|
||||
struct ImGuiTestEngine;
|
||||
|
||||
// Functions
|
||||
IMGUI_API void ImGuiTestEngine_ShowTestEngineWindows(ImGuiTestEngine* engine, bool* p_open);
|
||||
1306
libs/imgui_test_engine/imgui_te_utils.cpp
Normal file
1306
libs/imgui_test_engine/imgui_te_utils.cpp
Normal file
File diff suppressed because it is too large
Load Diff
221
libs/imgui_test_engine/imgui_te_utils.h
Normal file
221
libs/imgui_test_engine/imgui_te_utils.h
Normal file
@@ -0,0 +1,221 @@
|
||||
// dear imgui test engine
|
||||
// (helpers/utilities. do NOT use this as a general purpose library)
|
||||
|
||||
#pragma once
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <math.h> // fabsf
|
||||
#include <stdint.h> // uint64_t
|
||||
#include <stdio.h> // FILE*
|
||||
#include "imgui.h" // ImGuiID, ImGuiKey
|
||||
class Str; // Str<> from thirdparty/Str/Str.h
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Function Pointers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION
|
||||
#include <functional>
|
||||
#define ImFuncPtr(FUNC_TYPE) std::function<FUNC_TYPE>
|
||||
#else
|
||||
#define ImFuncPtr(FUNC_TYPE) FUNC_TYPE*
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Hashing Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ImGuiID ImHashDecoratedPath(const char* str, const char* str_end = NULL, ImGuiID seed = 0);
|
||||
const char* ImFindNextDecoratedPartInPath(const char* str, const char* str_end = NULL);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File/Directory Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool ImFileExist(const char* filename);
|
||||
bool ImFileDelete(const char* filename);
|
||||
bool ImFileCreateDirectoryChain(const char* path, const char* path_end = NULL);
|
||||
bool ImFileFindInParents(const char* sub_path, int max_parent_count, Str* output);
|
||||
bool ImFileLoadSourceBlurb(const char* filename, int line_no_start, int line_no_end, ImGuiTextBuffer* out_buf);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Path Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Those are strictly string manipulation functions
|
||||
const char* ImPathFindFilename(const char* path, const char* path_end = NULL); // Return value always between path and path_end
|
||||
const char* ImPathFindExtension(const char* path, const char* path_end = NULL); // Return value always between path and path_end
|
||||
void ImPathFixSeparatorsForCurrentOS(char* buf);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// String Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ImStrReplace(Str* s, const char* find, const char* repl);
|
||||
const char* ImStrchrRangeWithEscaping(const char* str, const char* str_end, char find_c);
|
||||
void ImStrXmlEscape(Str* s);
|
||||
int ImStrBase64Encode(const unsigned char* src, char* dst, int length);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parsing Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ImParseExtractArgcArgvFromCommandLine(int* out_argc, char const*** out_argv, const char* cmd_line);
|
||||
bool ImParseFindIniSection(const char* ini_config, const char* header, ImVector<char>* result);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Time Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
uint64_t ImTimeGetInMicroseconds();
|
||||
void ImTimestampToISO8601(uint64_t timestamp, Str* out_date);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Threading Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ImThreadSleepInMilliseconds(int ms);
|
||||
void ImThreadSetCurrentThreadDescription(const char* description);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Build Info helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// All the pointers are expect to be literals/persistent
|
||||
struct ImBuildInfo
|
||||
{
|
||||
const char* Type = "";
|
||||
const char* Cpu = "";
|
||||
const char* OS = "";
|
||||
const char* Compiler = "";
|
||||
char Date[32]; // "YYYY-MM-DD"
|
||||
const char* Time = "";
|
||||
};
|
||||
|
||||
const ImBuildInfo* ImBuildGetCompilationInfo();
|
||||
bool ImBuildFindGitBranchName(const char* git_repo_path, Str* branch_name);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Operating System Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
enum ImOsConsoleStream
|
||||
{
|
||||
ImOsConsoleStream_StandardOutput,
|
||||
ImOsConsoleStream_StandardError,
|
||||
};
|
||||
|
||||
enum ImOsConsoleTextColor
|
||||
{
|
||||
ImOsConsoleTextColor_Black,
|
||||
ImOsConsoleTextColor_White,
|
||||
ImOsConsoleTextColor_BrightWhite,
|
||||
ImOsConsoleTextColor_BrightRed,
|
||||
ImOsConsoleTextColor_BrightGreen,
|
||||
ImOsConsoleTextColor_BrightBlue,
|
||||
ImOsConsoleTextColor_BrightYellow,
|
||||
};
|
||||
|
||||
bool ImOsCreateProcess(const char* cmd_line);
|
||||
FILE* ImOsPOpen(const char* cmd_line, const char* mode);
|
||||
void ImOsPClose(FILE* fp);
|
||||
void ImOsOpenInShell(const char* path);
|
||||
bool ImOsIsDebuggerPresent();
|
||||
void ImOsOutputDebugString(const char* message);
|
||||
void ImOsConsoleSetTextColor(ImOsConsoleStream stream, ImOsConsoleTextColor color);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Miscellaneous functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Tables functions
|
||||
struct ImGuiTable;
|
||||
ImGuiID TableGetHeaderID(ImGuiTable* table, const char* column, int instance_no = 0);
|
||||
ImGuiID TableGetHeaderID(ImGuiTable* table, int column_n, int instance_no = 0);
|
||||
void TableDiscardInstanceAndSettings(ImGuiID table_id);
|
||||
|
||||
// DrawData functions
|
||||
void DrawDataVerifyMatchingBufferCount(ImDrawData* draw_data);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helper: maintain/calculate moving average
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template<typename TYPE>
|
||||
struct ImMovingAverage
|
||||
{
|
||||
// Internal Fields
|
||||
ImVector<TYPE> Samples;
|
||||
TYPE Accum;
|
||||
int Idx;
|
||||
int FillAmount;
|
||||
|
||||
// Functions
|
||||
ImMovingAverage() { Accum = (TYPE)0; Idx = FillAmount = 0; }
|
||||
void Init(int count) { Samples.resize(count); memset(Samples.Data, 0, (size_t)Samples.Size * sizeof(TYPE)); Accum = (TYPE)0; Idx = FillAmount = 0; }
|
||||
void AddSample(TYPE v) { Accum += v - Samples[Idx]; Samples[Idx] = v; if (++Idx == Samples.Size) Idx = 0; if (FillAmount < Samples.Size) FillAmount++; }
|
||||
TYPE GetAverage() const { return Accum / (TYPE)FillAmount; }
|
||||
int GetSampleCount() const { return Samples.Size; }
|
||||
bool IsFull() const { return FillAmount == Samples.Size; }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helper: Simple/dumb CSV parser
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct ImGuiCsvParser
|
||||
{
|
||||
// Public fields
|
||||
int Columns = 0; // Number of columns in CSV file.
|
||||
int Rows = 0; // Number of rows in CSV file.
|
||||
|
||||
// Internal fields
|
||||
char* _Data = NULL; // CSV file data.
|
||||
ImVector<char*> _Index; // CSV table: _Index[row * _Columns + col].
|
||||
|
||||
// Functions
|
||||
ImGuiCsvParser(int columns = -1) { Columns = columns; }
|
||||
~ImGuiCsvParser() { Clear(); }
|
||||
bool Load(const char* file_name); // Open and parse a CSV file.
|
||||
void Clear(); // Free allocated buffers.
|
||||
const char* GetCell(int row, int col) { IM_ASSERT(0 <= row && row < Rows && 0 <= col && col < Columns); return _Index[row * Columns + col]; }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Misc Dear ImGui extensions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if IMGUI_VERSION_NUM < 18924
|
||||
struct ImGuiTabBar;
|
||||
struct ImGuiTabItem;
|
||||
#endif
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
|
||||
IMGUI_API void ItemErrorFrame(ImU32 col);
|
||||
|
||||
#if IMGUI_VERSION_NUM < 18927
|
||||
ImGuiID TableGetInstanceID(ImGuiTable* table, int instance_no = 0);
|
||||
#endif
|
||||
|
||||
// Str support for InputText()
|
||||
IMGUI_API bool InputText(const char* label, Str* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, Str* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||
IMGUI_API bool InputTextMultiline(const char* label, Str* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||
|
||||
// Splitter
|
||||
IMGUI_API bool Splitter(const char* id, float* value_1, float* value_2, int axis, int anchor = 0, float min_size_0 = -1.0f, float min_size_1 = -1.0f);
|
||||
|
||||
// Misc
|
||||
IMGUI_API ImFont* FindFontByPrefix(const char* name);
|
||||
|
||||
// Legacy version support
|
||||
#if IMGUI_VERSION_NUM < 18924
|
||||
IMGUI_API const char* TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
|
||||
#endif
|
||||
|
||||
}
|
||||
7
libs/imgui_test_engine/thirdparty/README.md
vendored
Normal file
7
libs/imgui_test_engine/thirdparty/README.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
## Third party libraries used by Test Engine
|
||||
|
||||
Always used:
|
||||
- `Str/Str.h` simple string type, used by `imgui_test_engine` (Public Domain)
|
||||
|
||||
Used if `IMGUI_TEST_ENGINE_ENABLE_CAPTURE` is defined to 1 (default: 1)
|
||||
- `stb/imstb_image_write.h` image writer, used by `imgui_capture_tool` (MIT Licence OR Public Domain)
|
||||
71
libs/imgui_test_engine/thirdparty/Str/README.md
vendored
Normal file
71
libs/imgui_test_engine/thirdparty/Str/README.md
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
```
|
||||
Str
|
||||
Simple C++ string type with an optional local buffer, by Omar Cornut
|
||||
https://github.com/ocornut/str
|
||||
|
||||
LICENSE
|
||||
This software is in the public domain. Where that dedication is not
|
||||
recognized, you are granted a perpetual, irrevocable license to copy,
|
||||
distribute, and modify this file as you see fit.
|
||||
|
||||
USAGE
|
||||
Include Str.h in whatever places need to refer to it.
|
||||
In ONE .cpp file, write '#define STR_IMPLEMENTATION' before the #include.
|
||||
This expands out the actual implementation into that C/C++ file.
|
||||
|
||||
NOTES
|
||||
- This isn't a fully featured string class.
|
||||
- It is a simple, bearable replacement to std::string that isn't heap abusive nor bloated (can actually be debugged by humans!).
|
||||
- String are mutable. We don't maintain size so length() is not-constant time.
|
||||
- Maximum string size currently limited to 2 MB (we allocate 21 bits to hold capacity).
|
||||
- Local buffer size is currently limited to 1023 bytes (we allocate 10 bits to hold local buffer size).
|
||||
- We could easily raise those limits if we are ok to increase the structure overhead in 32-bits mode.
|
||||
- In "non-owned" mode for literals/reference we don't do any tracking/counting of references.
|
||||
- Overhead is 8-bytes in 32-bits, 16-bytes in 64-bits (12 + alignment).
|
||||
- I'm using this code but it hasn't been tested thoroughly.
|
||||
|
||||
The idea is that you can provide an arbitrary sized local buffer if you expect string to fit
|
||||
most of the time, and then you avoid using costly heap.
|
||||
|
||||
No local buffer, always use heap, sizeof()==8~16 (depends if your pointers are 32-bits or 64-bits)
|
||||
|
||||
Str s = "hey"; // use heap
|
||||
|
||||
With a local buffer of 16 bytes, sizeof() == 8~16 + 16 bytes.
|
||||
|
||||
Str16 s = "filename.h"; // copy into local buffer
|
||||
Str16 s = "long_filename_not_very_long_but_longer_than_expected.h"; // use heap
|
||||
|
||||
With a local buffer of 256 bytes, sizeof() == 8~16 + 256 bytes.
|
||||
|
||||
Str256 s = "long_filename_not_very_long_but_longer_than_expected.h"; // copy into local buffer
|
||||
|
||||
Common sizes are defined at the bottom of Str.h, you may define your own.
|
||||
|
||||
Functions:
|
||||
|
||||
Str256 s;
|
||||
s.set("hello sailor"); // set (copy)
|
||||
s.setf("%s/%s.tmp", folder, filename); // set (w/format)
|
||||
s.append("hello"); // append. cost a length() calculation!
|
||||
s.appendf("hello %d", 42); // append (w/format). cost a length() calculation!
|
||||
s.set_ref("Hey!"); // set (literal/reference, just copy pointer, no tracking)
|
||||
|
||||
Constructor helper for format string: add a trailing 'f' to the type. Underlying type is the same.
|
||||
|
||||
Str256f filename("%s/%s.tmp", folder, filename); // construct (w/format)
|
||||
fopen(Str256f("%s/%s.tmp, folder, filename).c_str(), "rb"); // construct (w/format), use as function param, destruct
|
||||
|
||||
Constructor helper for reference/literal:
|
||||
|
||||
StrRef ref("literal"); // copy pointer, no allocation, no string copy
|
||||
StrRef ref2(GetDebugName()); // copy pointer. no tracking of anything whatsoever, know what you are doing!
|
||||
|
||||
All StrXXX types derives from Str and instance hold the local buffer capacity.
|
||||
So you can pass e.g. Str256* to a function taking base type Str* and it will be functional!
|
||||
|
||||
void MyFunc(Str& s) { s = "Hello"; } // will use local buffer if available in Str instance
|
||||
|
||||
(Using a template e.g. Str<N> we could remove the LocalBufSize storage but it would make passing typed Str<> to functions tricky.
|
||||
Instead we don't use template so you can pass them around as the base type Str*. Also, templates are ugly.)
|
||||
```
|
||||
658
libs/imgui_test_engine/thirdparty/Str/Str.h
vendored
Normal file
658
libs/imgui_test_engine/thirdparty/Str/Str.h
vendored
Normal file
@@ -0,0 +1,658 @@
|
||||
// Str v0.32
|
||||
// Simple C++ string type with an optional local buffer, by Omar Cornut
|
||||
// https://github.com/ocornut/str
|
||||
|
||||
// LICENSE
|
||||
// This software is in the public domain. Where that dedication is not
|
||||
// recognized, you are granted a perpetual, irrevocable license to copy,
|
||||
// distribute, and modify this file as you see fit.
|
||||
|
||||
// USAGE
|
||||
// Include this file in whatever places need to refer to it.
|
||||
// In ONE .cpp file, write '#define STR_IMPLEMENTATION' before the #include of this file.
|
||||
// This expands out the actual implementation into that C/C++ file.
|
||||
|
||||
|
||||
/*
|
||||
- This isn't a fully featured string class.
|
||||
- It is a simple, bearable replacement to std::string that isn't heap abusive nor bloated (can actually be debugged by humans).
|
||||
- String are mutable. We don't maintain size so length() is not-constant time.
|
||||
- Maximum string size currently limited to 2 MB (we allocate 21 bits to hold capacity).
|
||||
- Local buffer size is currently limited to 1023 bytes (we allocate 10 bits to hold local buffer size).
|
||||
- In "non-owned" mode for literals/reference we don't do any tracking/counting of references.
|
||||
- Overhead is 8-bytes in 32-bits, 16-bytes in 64-bits (12 + alignment).
|
||||
- This code hasn't been tested very much. it is probably incomplete or broken. Made it for my own use.
|
||||
|
||||
The idea is that you can provide an arbitrary sized local buffer if you expect string to fit
|
||||
most of the time, and then you avoid using costly heap.
|
||||
|
||||
No local buffer, always use heap, sizeof()==8~16 (depends if your pointers are 32-bits or 64-bits)
|
||||
|
||||
Str s = "hey";
|
||||
|
||||
With a local buffer of 16 bytes, sizeof() == 8~16 + 16 bytes.
|
||||
|
||||
Str16 s = "filename.h"; // copy into local buffer
|
||||
Str16 s = "long_filename_not_very_long_but_longer_than_expected.h"; // use heap
|
||||
|
||||
With a local buffer of 256 bytes, sizeof() == 8~16 + 256 bytes.
|
||||
|
||||
Str256 s = "long_filename_not_very_long_but_longer_than_expected.h"; // copy into local buffer
|
||||
|
||||
Common sizes are defined at the bottom of Str.h, you may define your own.
|
||||
|
||||
Functions:
|
||||
|
||||
Str256 s;
|
||||
s.set("hello sailor"); // set (copy)
|
||||
s.setf("%s/%s.tmp", folder, filename); // set (w/format)
|
||||
s.append("hello"); // append. cost a length() calculation!
|
||||
s.appendf("hello %d", 42); // append (w/format). cost a length() calculation!
|
||||
s.set_ref("Hey!"); // set (literal/reference, just copy pointer, no tracking)
|
||||
|
||||
Constructor helper for format string: add a trailing 'f' to the type. Underlying type is the same.
|
||||
|
||||
Str256f filename("%s/%s.tmp", folder, filename); // construct (w/format)
|
||||
fopen(Str256f("%s/%s.tmp, folder, filename).c_str(), "rb"); // construct (w/format), use as function param, destruct
|
||||
|
||||
Constructor helper for reference/literal:
|
||||
|
||||
StrRef ref("literal"); // copy pointer, no allocation, no string copy
|
||||
StrRef ref2(GetDebugName()); // copy pointer. no tracking of anything whatsoever, know what you are doing!
|
||||
|
||||
All StrXXX types derives from Str and instance hold the local buffer capacity. So you can pass e.g. Str256* to a function taking base type Str* and it will be functional.
|
||||
|
||||
void MyFunc(Str& s) { s = "Hello"; } // will use local buffer if available in Str instance
|
||||
|
||||
(Using a template e.g. Str<N> we could remove the LocalBufSize storage but it would make passing typed Str<> to functions tricky.
|
||||
Instead we don't use template so you can pass them around as the base type Str*. Also, templates are ugly.)
|
||||
*/
|
||||
|
||||
/*
|
||||
CHANGELOG
|
||||
0.32 - added owned() accessor.
|
||||
0.31 - fixed various warnings.
|
||||
0.30 - turned into a single header file, removed Str.cpp.
|
||||
0.29 - fixed bug when calling reserve on non-owned strings (ie. when using StrRef or set_ref), and fixed <string> include.
|
||||
0.28 - breaking change: replaced Str32 by Str30 to avoid collision with Str32 from MacTypes.h .
|
||||
0.27 - added STR_API and basic .natvis file.
|
||||
0.26 - fixed set(cont char* src, const char* src_end) writing null terminator to the wrong position.
|
||||
0.25 - allow set(const char* NULL) or operator= NULL to clear the string. note that set() from range or other types are not allowed.
|
||||
0.24 - allow set_ref(const char* NULL) to clear the string. include fixes for linux.
|
||||
0.23 - added append(char). added append_from(int idx, XXX) functions. fixed some compilers warnings.
|
||||
0.22 - documentation improvements, comments. fixes for some compilers.
|
||||
0.21 - added StrXXXf() constructor to construct directly from a format string.
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO
|
||||
- Since we lose 4-bytes of padding on 64-bits architecture, perhaps just spread the header to 8-bytes and lift size limits?
|
||||
- More functions/helpers.
|
||||
*/
|
||||
|
||||
#ifndef STR_INCLUDED
|
||||
#define STR_INCLUDED
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// CONFIGURATION
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
#ifndef STR_MEMALLOC
|
||||
#define STR_MEMALLOC malloc
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifndef STR_MEMFREE
|
||||
#define STR_MEMFREE free
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifndef STR_ASSERT
|
||||
#define STR_ASSERT assert
|
||||
#include <assert.h>
|
||||
#endif
|
||||
#ifndef STR_API
|
||||
#define STR_API
|
||||
#endif
|
||||
#include <stdarg.h> // for va_list
|
||||
#include <string.h> // for strlen, strcmp, memcpy, etc.
|
||||
|
||||
// Configuration: #define STR_SUPPORT_STD_STRING 0 to disable setters variants using const std::string& (on by default)
|
||||
#ifndef STR_SUPPORT_STD_STRING
|
||||
#define STR_SUPPORT_STD_STRING 1
|
||||
#endif
|
||||
|
||||
// Configuration: #define STR_DEFINE_STR32 1 to keep defining Str32/Str32f, but be warned: on macOS/iOS, MacTypes.h also defines a type named Str32.
|
||||
#ifndef STR_DEFINE_STR32
|
||||
#define STR_DEFINE_STR32 0
|
||||
#endif
|
||||
|
||||
#if STR_SUPPORT_STD_STRING
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// HEADERS
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// This is the base class that you can pass around
|
||||
// Footprint is 8-bytes (32-bits arch) or 16-bytes (64-bits arch)
|
||||
class STR_API Str
|
||||
{
|
||||
char* Data; // Point to LocalBuf() or heap allocated
|
||||
int Capacity : 21; // Max 2 MB
|
||||
int LocalBufSize : 10; // Max 1023 bytes
|
||||
unsigned int Owned : 1; // Set when we have ownership of the pointed data (most common, unless using set_ref() method or StrRef constructor)
|
||||
|
||||
public:
|
||||
inline char* c_str() { return Data; }
|
||||
inline const char* c_str() const { return Data; }
|
||||
inline bool empty() const { return Data[0] == 0; }
|
||||
inline int length() const { return (int)strlen(Data); } // by design, allow user to write into the buffer at any time
|
||||
inline int capacity() const { return Capacity; }
|
||||
inline bool owned() const { return Owned ? true : false; }
|
||||
|
||||
inline void set_ref(const char* src);
|
||||
int setf(const char* fmt, ...);
|
||||
int setfv(const char* fmt, va_list args);
|
||||
int setf_nogrow(const char* fmt, ...);
|
||||
int setfv_nogrow(const char* fmt, va_list args);
|
||||
int append(char c);
|
||||
int append(const char* s, const char* s_end = NULL);
|
||||
int appendf(const char* fmt, ...);
|
||||
int appendfv(const char* fmt, va_list args);
|
||||
int append_from(int idx, char c);
|
||||
int append_from(int idx, const char* s, const char* s_end = NULL); // If you know the string length or want to append from a certain point
|
||||
int appendf_from(int idx, const char* fmt, ...);
|
||||
int appendfv_from(int idx, const char* fmt, va_list args);
|
||||
|
||||
void clear();
|
||||
void reserve(int cap);
|
||||
void reserve_discard(int cap);
|
||||
void shrink_to_fit();
|
||||
|
||||
inline char& operator[](size_t i) { return Data[i]; }
|
||||
inline char operator[](size_t i) const { return Data[i]; }
|
||||
//explicit operator const char*() const{ return Data; }
|
||||
|
||||
inline Str();
|
||||
inline Str(const char* rhs);
|
||||
inline void set(const char* src);
|
||||
inline void set(const char* src, const char* src_end);
|
||||
inline Str& operator=(const char* rhs) { set(rhs); return *this; }
|
||||
inline bool operator==(const char* rhs) const { return strcmp(c_str(), rhs) == 0; }
|
||||
|
||||
inline Str(const Str& rhs);
|
||||
inline void set(const Str& src);
|
||||
inline Str& operator=(const Str& rhs) { set(rhs); return *this; }
|
||||
inline bool operator==(const Str& rhs) const { return strcmp(c_str(), rhs.c_str()) == 0; }
|
||||
|
||||
#if STR_SUPPORT_STD_STRING
|
||||
inline Str(const std::string& rhs);
|
||||
inline void set(const std::string& src);
|
||||
inline Str& operator=(const std::string& rhs) { set(rhs); return *this; }
|
||||
inline bool operator==(const std::string& rhs)const { return strcmp(c_str(), rhs.c_str()) == 0; }
|
||||
#endif
|
||||
|
||||
// Destructor for all variants
|
||||
inline ~Str()
|
||||
{
|
||||
if (Owned && !is_using_local_buf())
|
||||
STR_MEMFREE(Data);
|
||||
}
|
||||
|
||||
static char* EmptyBuffer;
|
||||
|
||||
protected:
|
||||
inline char* local_buf() { return (char*)this + sizeof(Str); }
|
||||
inline const char* local_buf() const { return (char*)this + sizeof(Str); }
|
||||
inline bool is_using_local_buf() const { return Data == local_buf() && LocalBufSize != 0; }
|
||||
|
||||
// Constructor for StrXXX variants with local buffer
|
||||
Str(unsigned short local_buf_size)
|
||||
{
|
||||
STR_ASSERT(local_buf_size < 1024);
|
||||
Data = local_buf();
|
||||
Data[0] = '\0';
|
||||
Capacity = local_buf_size;
|
||||
LocalBufSize = local_buf_size;
|
||||
Owned = 1;
|
||||
}
|
||||
};
|
||||
|
||||
void Str::set(const char* src)
|
||||
{
|
||||
// We allow set(NULL) or via = operator to clear the string.
|
||||
if (src == NULL)
|
||||
{
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
int buf_len = (int)strlen(src)+1;
|
||||
if (Capacity < buf_len)
|
||||
reserve_discard(buf_len);
|
||||
memcpy(Data, src, (size_t)buf_len);
|
||||
Owned = 1;
|
||||
}
|
||||
|
||||
void Str::set(const char* src, const char* src_end)
|
||||
{
|
||||
STR_ASSERT(src != NULL && src_end >= src);
|
||||
int buf_len = (int)(src_end-src)+1;
|
||||
if ((int)Capacity < buf_len)
|
||||
reserve_discard(buf_len);
|
||||
memcpy(Data, src, (size_t)(buf_len - 1));
|
||||
Data[buf_len-1] = 0;
|
||||
Owned = 1;
|
||||
}
|
||||
|
||||
void Str::set(const Str& src)
|
||||
{
|
||||
int buf_len = (int)strlen(src.c_str())+1;
|
||||
if ((int)Capacity < buf_len)
|
||||
reserve_discard(buf_len);
|
||||
memcpy(Data, src.c_str(), (size_t)buf_len);
|
||||
Owned = 1;
|
||||
}
|
||||
|
||||
#if STR_SUPPORT_STD_STRING
|
||||
void Str::set(const std::string& src)
|
||||
{
|
||||
int buf_len = (int)src.length()+1;
|
||||
if ((int)Capacity < buf_len)
|
||||
reserve_discard(buf_len);
|
||||
memcpy(Data, src.c_str(), (size_t)buf_len);
|
||||
Owned = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void Str::set_ref(const char* src)
|
||||
{
|
||||
if (Owned && !is_using_local_buf())
|
||||
STR_MEMFREE(Data);
|
||||
Data = src ? (char*)src : EmptyBuffer;
|
||||
Capacity = 0;
|
||||
Owned = 0;
|
||||
}
|
||||
|
||||
Str::Str()
|
||||
{
|
||||
Data = EmptyBuffer; // Shared READ-ONLY initial buffer for 0 capacity
|
||||
Capacity = 0;
|
||||
LocalBufSize = 0;
|
||||
Owned = 0;
|
||||
}
|
||||
|
||||
Str::Str(const Str& rhs) : Str()
|
||||
{
|
||||
set(rhs);
|
||||
}
|
||||
|
||||
Str::Str(const char* rhs) : Str()
|
||||
{
|
||||
set(rhs);
|
||||
}
|
||||
|
||||
#if STR_SUPPORT_STD_STRING
|
||||
Str::Str(const std::string& rhs) : Str()
|
||||
{
|
||||
set(rhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Literal/reference string
|
||||
class StrRef : public Str
|
||||
{
|
||||
public:
|
||||
StrRef(const char* s) : Str() { set_ref(s); }
|
||||
};
|
||||
|
||||
// Types embedding a local buffer
|
||||
// NB: we need to override the constructor and = operator for both Str& and TYPENAME (without the later compiler will call a default copy operator)
|
||||
#if STR_SUPPORT_STD_STRING
|
||||
|
||||
#define STR_DEFINETYPE(TYPENAME, LOCALBUFSIZE) \
|
||||
class TYPENAME : public Str \
|
||||
{ \
|
||||
char local_buf[LOCALBUFSIZE]; \
|
||||
public: \
|
||||
TYPENAME() : Str(LOCALBUFSIZE) {} \
|
||||
TYPENAME(const Str& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
|
||||
TYPENAME(const char* rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
|
||||
TYPENAME(const TYPENAME& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
|
||||
TYPENAME(const std::string& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
|
||||
TYPENAME& operator=(const char* rhs) { set(rhs); return *this; } \
|
||||
TYPENAME& operator=(const Str& rhs) { set(rhs); return *this; } \
|
||||
TYPENAME& operator=(const TYPENAME& rhs) { set(rhs); return *this; } \
|
||||
TYPENAME& operator=(const std::string& rhs) { set(rhs); return *this; } \
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#define STR_DEFINETYPE(TYPENAME, LOCALBUFSIZE) \
|
||||
class TYPENAME : public Str \
|
||||
{ \
|
||||
char local_buf[LOCALBUFSIZE]; \
|
||||
public: \
|
||||
TYPENAME() : Str(LOCALBUFSIZE) {} \
|
||||
TYPENAME(const Str& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
|
||||
TYPENAME(const char* rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
|
||||
TYPENAME(const TYPENAME& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
|
||||
TYPENAME& operator=(const char* rhs) { set(rhs); return *this; } \
|
||||
TYPENAME& operator=(const Str& rhs) { set(rhs); return *this; } \
|
||||
TYPENAME& operator=(const TYPENAME& rhs) { set(rhs); return *this; } \
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// Disable PVS-Studio warning V730: Not all members of a class are initialized inside the constructor (local_buf is not initialized and that is fine)
|
||||
// -V:STR_DEFINETYPE:730
|
||||
|
||||
// Helper to define StrXXXf constructors
|
||||
#define STR_DEFINETYPE_F(TYPENAME, TYPENAME_F) \
|
||||
class TYPENAME_F : public TYPENAME \
|
||||
{ \
|
||||
public: \
|
||||
TYPENAME_F(const char* fmt, ...) : TYPENAME() { va_list args; va_start(args, fmt); setfv(fmt, args); va_end(args); } \
|
||||
};
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-private-field" // warning : private field 'local_buf' is not used
|
||||
#endif
|
||||
|
||||
// Declaring types for common sizes here
|
||||
STR_DEFINETYPE(Str16, 16)
|
||||
STR_DEFINETYPE(Str30, 30)
|
||||
STR_DEFINETYPE(Str64, 64)
|
||||
STR_DEFINETYPE(Str128, 128)
|
||||
STR_DEFINETYPE(Str256, 256)
|
||||
STR_DEFINETYPE(Str512, 512)
|
||||
|
||||
// Declaring helper constructors to pass in format strings in one statement
|
||||
STR_DEFINETYPE_F(Str16, Str16f)
|
||||
STR_DEFINETYPE_F(Str30, Str30f)
|
||||
STR_DEFINETYPE_F(Str64, Str64f)
|
||||
STR_DEFINETYPE_F(Str128, Str128f)
|
||||
STR_DEFINETYPE_F(Str256, Str256f)
|
||||
STR_DEFINETYPE_F(Str512, Str512f)
|
||||
|
||||
#if STR_DEFINE_STR32
|
||||
STR_DEFINETYPE(Str32, 32)
|
||||
STR_DEFINETYPE_F(Str32, Str32f)
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // #ifndef STR_INCLUDED
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// IMPLEMENTATION
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
#ifdef STR_IMPLEMENTATION
|
||||
|
||||
#include <stdio.h> // for vsnprintf
|
||||
|
||||
// On some platform vsnprintf() takes va_list by reference and modifies it.
|
||||
// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
|
||||
#ifndef va_copy
|
||||
#define va_copy(dest, src) (dest = src)
|
||||
#endif
|
||||
|
||||
// Static empty buffer we can point to for empty strings
|
||||
// Pointing to a literal increases the like-hood of getting a crash if someone attempts to write in the empty string buffer.
|
||||
char* Str::EmptyBuffer = (char*)"\0NULL";
|
||||
|
||||
// Clear
|
||||
void Str::clear()
|
||||
{
|
||||
if (Owned && !is_using_local_buf())
|
||||
STR_MEMFREE(Data);
|
||||
if (LocalBufSize)
|
||||
{
|
||||
Data = local_buf();
|
||||
Data[0] = '\0';
|
||||
Capacity = LocalBufSize;
|
||||
Owned = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Data = EmptyBuffer;
|
||||
Capacity = 0;
|
||||
Owned = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve memory, preserving the current of the buffer
|
||||
void Str::reserve(int new_capacity)
|
||||
{
|
||||
if (new_capacity <= Capacity)
|
||||
return;
|
||||
|
||||
char* new_data;
|
||||
if (new_capacity < LocalBufSize)
|
||||
{
|
||||
// Disowned -> LocalBuf
|
||||
new_data = local_buf();
|
||||
new_capacity = LocalBufSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disowned or LocalBuf -> Heap
|
||||
new_data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
|
||||
}
|
||||
|
||||
// string in Data might be longer than new_capacity if it wasn't owned, don't copy too much
|
||||
#ifdef _MSC_VER
|
||||
strncpy_s(new_data, (size_t)new_capacity, Data, (size_t)new_capacity - 1);
|
||||
#else
|
||||
strncpy(new_data, Data, (size_t)new_capacity - 1);
|
||||
#endif
|
||||
new_data[new_capacity - 1] = 0;
|
||||
|
||||
if (Owned && !is_using_local_buf())
|
||||
STR_MEMFREE(Data);
|
||||
|
||||
Data = new_data;
|
||||
Capacity = new_capacity;
|
||||
Owned = 1;
|
||||
}
|
||||
|
||||
// Reserve memory, discarding the current of the buffer (if we expect to be fully rewritten)
|
||||
void Str::reserve_discard(int new_capacity)
|
||||
{
|
||||
if (new_capacity <= Capacity)
|
||||
return;
|
||||
|
||||
if (Owned && !is_using_local_buf())
|
||||
STR_MEMFREE(Data);
|
||||
|
||||
if (new_capacity < LocalBufSize)
|
||||
{
|
||||
// Disowned -> LocalBuf
|
||||
Data = local_buf();
|
||||
Capacity = LocalBufSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disowned or LocalBuf -> Heap
|
||||
Data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
|
||||
Capacity = new_capacity;
|
||||
}
|
||||
Owned = 1;
|
||||
}
|
||||
|
||||
void Str::shrink_to_fit()
|
||||
{
|
||||
if (!Owned || is_using_local_buf())
|
||||
return;
|
||||
int new_capacity = length() + 1;
|
||||
if (Capacity <= new_capacity)
|
||||
return;
|
||||
|
||||
char* new_data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
|
||||
memcpy(new_data, Data, (size_t)new_capacity);
|
||||
STR_MEMFREE(Data);
|
||||
Data = new_data;
|
||||
Capacity = new_capacity;
|
||||
}
|
||||
|
||||
// FIXME: merge setfv() and appendfv()?
|
||||
int Str::setfv(const char* fmt, va_list args)
|
||||
{
|
||||
// Needed for portability on platforms where va_list are passed by reference and modified by functions
|
||||
va_list args2;
|
||||
va_copy(args2, args);
|
||||
|
||||
// MSVC returns -1 on overflow when writing, which forces us to do two passes
|
||||
// FIXME-OPT: Find a way around that.
|
||||
#ifdef _MSC_VER
|
||||
int len = vsnprintf(NULL, 0, fmt, args);
|
||||
STR_ASSERT(len >= 0);
|
||||
|
||||
if (Capacity < len + 1)
|
||||
reserve_discard(len + 1);
|
||||
len = vsnprintf(Data, len + 1, fmt, args2);
|
||||
#else
|
||||
// First try
|
||||
int len = vsnprintf(Owned ? Data : NULL, Owned ? (size_t)Capacity : 0, fmt, args);
|
||||
STR_ASSERT(len >= 0);
|
||||
|
||||
if (Capacity < len + 1)
|
||||
{
|
||||
reserve_discard(len + 1);
|
||||
len = vsnprintf(Data, (size_t)len + 1, fmt, args2);
|
||||
}
|
||||
#endif
|
||||
|
||||
STR_ASSERT(Owned);
|
||||
return len;
|
||||
}
|
||||
|
||||
int Str::setf(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = setfv(fmt, args);
|
||||
va_end(args);
|
||||
return len;
|
||||
}
|
||||
|
||||
int Str::setfv_nogrow(const char* fmt, va_list args)
|
||||
{
|
||||
STR_ASSERT(Owned);
|
||||
|
||||
if (Capacity == 0)
|
||||
return 0;
|
||||
|
||||
int w = vsnprintf(Data, (size_t)Capacity, fmt, args);
|
||||
Data[Capacity - 1] = 0;
|
||||
Owned = 1;
|
||||
return (w == -1) ? Capacity - 1 : w;
|
||||
}
|
||||
|
||||
int Str::setf_nogrow(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = setfv_nogrow(fmt, args);
|
||||
va_end(args);
|
||||
return len;
|
||||
}
|
||||
|
||||
int Str::append_from(int idx, char c)
|
||||
{
|
||||
int add_len = 1;
|
||||
if (Capacity < idx + add_len + 1)
|
||||
reserve(idx + add_len + 1);
|
||||
Data[idx] = c;
|
||||
Data[idx + add_len] = 0;
|
||||
STR_ASSERT(Owned);
|
||||
return add_len;
|
||||
}
|
||||
|
||||
int Str::append_from(int idx, const char* s, const char* s_end)
|
||||
{
|
||||
if (!s_end)
|
||||
s_end = s + strlen(s);
|
||||
int add_len = (int)(s_end - s);
|
||||
if (Capacity < idx + add_len + 1)
|
||||
reserve(idx + add_len + 1);
|
||||
memcpy(Data + idx, (const void*)s, (size_t)add_len);
|
||||
Data[idx + add_len] = 0; // Our source data isn't necessarily zero-terminated
|
||||
STR_ASSERT(Owned);
|
||||
return add_len;
|
||||
}
|
||||
|
||||
// FIXME: merge setfv() and appendfv()?
|
||||
int Str::appendfv_from(int idx, const char* fmt, va_list args)
|
||||
{
|
||||
// Needed for portability on platforms where va_list are passed by reference and modified by functions
|
||||
va_list args2;
|
||||
va_copy(args2, args);
|
||||
|
||||
// MSVC returns -1 on overflow when writing, which forces us to do two passes
|
||||
// FIXME-OPT: Find a way around that.
|
||||
#ifdef _MSC_VER
|
||||
int add_len = vsnprintf(NULL, 0, fmt, args);
|
||||
STR_ASSERT(add_len >= 0);
|
||||
|
||||
if (Capacity < idx + add_len + 1)
|
||||
reserve(idx + add_len + 1);
|
||||
add_len = vsnprintf(Data + idx, add_len + 1, fmt, args2);
|
||||
#else
|
||||
// First try
|
||||
int add_len = vsnprintf(Owned ? Data + idx : NULL, Owned ? (size_t)(Capacity - idx) : 0, fmt, args);
|
||||
STR_ASSERT(add_len >= 0);
|
||||
|
||||
if (Capacity < idx + add_len + 1)
|
||||
{
|
||||
reserve(idx + add_len + 1);
|
||||
add_len = vsnprintf(Data + idx, (size_t)add_len + 1, fmt, args2);
|
||||
}
|
||||
#endif
|
||||
|
||||
STR_ASSERT(Owned);
|
||||
return add_len;
|
||||
}
|
||||
|
||||
int Str::appendf_from(int idx, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = appendfv_from(idx, fmt, args);
|
||||
va_end(args);
|
||||
return len;
|
||||
}
|
||||
|
||||
int Str::append(char c)
|
||||
{
|
||||
int cur_len = length();
|
||||
return append_from(cur_len, c);
|
||||
}
|
||||
|
||||
int Str::append(const char* s, const char* s_end)
|
||||
{
|
||||
int cur_len = length();
|
||||
return append_from(cur_len, s, s_end);
|
||||
}
|
||||
|
||||
int Str::appendfv(const char* fmt, va_list args)
|
||||
{
|
||||
int cur_len = length();
|
||||
return appendfv_from(cur_len, fmt, args);
|
||||
}
|
||||
|
||||
int Str::appendf(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = appendfv(fmt, args);
|
||||
va_end(args);
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif // #define STR_IMPLEMENTATION
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
1617
libs/imgui_test_engine/thirdparty/stb/imstb_image_write.h
vendored
Normal file
1617
libs/imgui_test_engine/thirdparty/stb/imstb_image_write.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user