Giter VIP home page Giter VIP logo

imgui_test_engine's Introduction

Dear ImGui Test Engine + Test Suite

Automation system for Dear ImGui and applications/games/engines using Dear ImGui.

Because Dear ImGui tends to be wired in low-level subsystems within your codebase, you can leverage that to automate actions interacting with them.

Contents

Don't blink

Running Dear ImGui Test Suite, in Fast Mode, in a visible window:

Running_Dear_ImGui_Test_Suite_.Fast_Mode.mp4

Quick overview of what automation code may look like like:

ImGuiTest* test = IM_REGISTER_TEST(e, "demo_test", "test1");
test->TestFunc = [](ImGuiTestContext* ctx)
{
    ctx->SetRef("My Window");           // Set a base path so we don't have to specify full path afterwards
    ctx->ItemClick("My Button");        // Click "My Button" inside "My Window"
    ctx->ItemCheck("Node/Checkbox");    // Open "Node", find "Checkbox", ensure it is checked if not checked already.
    ctx->ItemInputValue("Slider", 123); // Find "Slider" and set the value to 123
    IM_CHECK_EQ(app->SliderValue, 123); // Check value on app side
    
    ctx->MenuCheck("//Dear ImGui Demo/Tools/About Dear ImGui"); // Show Dear ImGui About Window (assume Demo window is open)
};

Overview

  • Designed to automate and test Dear ImGui applications.
  • We also use it to self-test Dear ImGui itself, reduce regression and facilitate contributions.
  • Test Engine interacts mostly from the point of view of an end-user, by injecting mouse/keyboard/gamepad inputs into Dear ImGui's IO. It means it tries to "find its way" toward accomplishing an action. Opening an item may mean CTRL+Tabbing into a given widow, moving things out of the way, scrolling to locate the item, querying its open status, etc.
  • It can be used for a variety of testing (smoke testing, integration/functional testing) or automation purposes (running tasks, capturing videos, etc.).
  • Your app can be controlled from Dear ImGui + You can now automate your app = You can test anything exposed in your app/engine! (not only UI).
  • It can run in your windowed application. It can also run headless (e.g. running GUI tests from a console or on a CI server without rendering).
  • It can run at simulated human speed (for watching or exporting videos) or can run in fast mode (e.g. teleporting mouse).
  • It can export screenshots and videos/gifs. They can be leveraged for some forms of testing, but also to generate assets for documentation, or notify teams of certain changes. Assets that often need to be updated are best generated from a script inside of manually recreated/cropped/exported.
  • You can use it to program high-level commands e.g. MenuCheck("Edit/Options/Enable Grid") or run more programmatic queries ("list openable items in that section, then open them all"). So from your POV it could be used for simple smoke testing ("open all our tools") or for more elaborate testing ("interact with xxx and xxx, check result").
  • It can be used as a form of "live tutorial / demo" where a script can run on an actual user application to showcase features.
  • It includes a performance tool and viewer which we used to record/compare performances between builds and branches (optional, requires ImPlot).

Status

  • It is currently a C++ API but down the line we can expect that the commands will be better standardized, stored in data files, called from other languages.
  • It has been in use and development since 2018 and only made public at the end of 2022. We'll provide best effort to make it suitable for user needs.
  • You will run into problems and shortcomings. We are happy to hear about them and improve the software and documentation accordingly.

Documentation

See Wiki sections:

Licenses

  • The imgui_test_engine/ folder is under the Dear ImGui Test Engine License.
    TL;DR: free for individuals, educational, open-source and small businesses uses. Paid for larger businesses. Read license for details. License sales to larger businesses are used to fund and sustain the development of Dear ImGui.
  • Other folders are all under the MIT License.

imgui_test_engine's People

Contributors

ajweeks avatar akmal-threepointsix avatar alzathar avatar elect86 avatar jacobfriedman avatar lamarche05 avatar ocornut avatar pathogendavid avatar potocpav avatar pthom avatar rokups avatar shironekoben avatar squareys avatar thedmd avatar xipiryon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

imgui_test_engine's Issues

Compilation error on Windows GCC/MingW because code uses try-except statement

image
Hello, I am getting this error when compiling my app with gcc. So try-except statement is a Microsoft extension that gcc does not seem to support.

image
The try-except statement is used in function ImThreadSetCurrentThreadDescriptionWin32OldStyle which is defined in file imgui_te_utils.cpp.

I suggest changing the test to #ifdef _MSC_VER so that the function would be excluded when compiling with gcc. This function is used only one time in function ImThreadSetCurrentThreadDescription, there also the usage of the problematic function should be conditional. After I added my proposed changes I was able to compile and run my app.

TestSuite: Framebuffer Mismatches on HiDPI (OSX) Laptop screen

Heads up - I'm using GLFW on OSX Ventura (M1, hiDPI) and the framebuffer is mismatched in the capture since I removed my normal-DPI monitor:

imgui_capture_0001

It's of a single window (capture tool).
Should look like (screenshot)
Screenshot 2023-01-25 at 2 37 31 PM.

I'm supposingglfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE) would solve it ... it's undefined for me as of this time. Looking into it.

Test suite can't find ImGui Git branch if it's not on HEAD

How to reproduce
Execute a git checkout on an arbitrary commit for ImGui (in my case I went back to the 1.90.1 version) and then execute ./imgui_test_suite -gui

In a summary:

cd imgui
git checkout d6cb3c9
cd ../imgui_test_engine/imgui_test_suite
make clean all
./imgui_test_suite -gui

Effects
This will break the execution in the line 839 of the file imgui_te_utils.cpp because the file imgui/.git/HEAD hasn't the hardcoded pattern const char prefix[] = "ref: refs/heads/"; but only the hash of the commit.
It doesn't crash but it prints the message Dear ImGui git repository was not found.

Race condition

In demo_misc_001 I have a breakpoint here at ctx->ItemHold("Scrolling/>>", 1.0f)

Then if I set a breakpoint at the previous line here at ctx->ItemOpen("Layout & Scrolling") the code will hit later one this LogDebug("BringWindowToDisplayFront, otherwise if there is no breakpoint, then there will be no LogDebug

Both (imgui and test) at 1.89.7

TestEngine: heap use after free in ItemDragAndDrop

Testcode at

https://github.com/vengi-voxel/vengi/blob/6916070fd1a4f246e711eec7a23ea642b6e63d89/src/tools/voxedit/modules/voxedit-ui/tests/AssetPanelTest.cpp#L14

The first Run-all worked fine, the second lead to this crash

void AssetPanel::registerUITests(ImGuiTestEngine *engine, const char *title) {
	IM_REGISTER_TEST(engine, testCategory(), "drag drop image")->TestFunc = [=](ImGuiTestContext *ctx) {
		if (_texturePool->cache().empty()) {
			ctx->LogInfo("No images found in asset panel");
			return;
		}
		const int viewportId = viewportEditMode(ctx, _app);
		IM_CHECK_SILENT(viewportId != -1);
		const core::String id = core::String::format("//%s", Viewport::viewportId(viewportId).c_str());

		const size_t n = core_min(3, _texturePool->cache().size());
		for (size_t i = 0; i < n; ++i) {
			IM_CHECK(focusWindow(ctx, title));
			ctx->ItemClick("##assetpaneltabs/Images");
			const core::String srcRef = core::string::format("##assetpaneltabs/Images/%i", (int)i);
			ctx->ItemDragAndDrop(srcRef.c_str(), id.c_str());
		}
	};
}

Maybe I'm doing something wrong here - but as the memory was allocated by testengine and also freed by testengine I thought it would make sense to report it.

=================================================================
==154641==ERROR: AddressSanitizer: heap-use-after-free on address 0x50f00004c618 at pc 0x557aa63d8ca6 bp 0x7f37b765f710 sp 0x7f37b765f708
READ of size 4 at 0x50f00004c618 thread T10
    #0 0x557aa63d8ca5 in ImGuiTestContext::ItemDragAndDrop(ImGuiTestRef, ImGuiTestRef, int) /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_context.cpp:3032
    #1 0x557aa3e19e30 in operator() /home/mgerhardy/dev/oss/engine/src/tools/voxedit/modules/voxedit-ui/tests/AssetPanelTest.cpp:27
    #2 0x557aa3e1b03e in __invoke_impl<void, voxedit::AssetPanel::registerUITests(ImGuiTestEngine*, char const*)::<lambda(ImGuiTestContext*)>&, ImGuiTestContext*> /usr/include/c++/13/bits/invoke.h:61
    #3 0x557aa3e1ac49 in __invoke_r<void, voxedit::AssetPanel::registerUITests(ImGuiTestEngine*, char const*)::<lambda(ImGuiTestContext*)>&, ImGuiTestContext*> /usr/include/c++/13/bits/invoke.h:111
    #4 0x557aa3e1a95c in _M_invoke /usr/include/c++/13/bits/std_function.h:290
    #5 0x557aa6439256 in std::function<void (ImGuiTestContext*)>::operator()(ImGuiTestContext*) const (/home/mgerhardy/dev/oss/engine/build/voxedit/vengi-voxedit+0x86ff256) (BuildId: aa8b0276cbe3a526c136e6b69891c16b23fd7758)
    #6 0x557aa64123b3 in ImGuiTestEngine_RunTest(ImGuiTestEngine*, ImGuiTestContext*, ImGuiTest*, int) /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_engine.cpp:1625
    #7 0x557aa64093b4 in ImGuiTestEngine_ProcessTestQueue /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_engine.cpp:1092
    #8 0x557aa6406623 in ImGuiTestEngine_TestQueueCoroutineMain /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_engine.cpp:909
    #9 0x557aa64c171b in CoroutineThreadMain /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_coroutine.cpp:57
    #10 0x557aa64c69f1 in void std::__invoke_impl<void, void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*>(std::__invoke_other, void (*&&)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*&&, void (*&&)(void*), void*&&) /usr/include/c++/13/bits/invoke.h:61
    #11 0x557aa64c66e2 in std::__invoke_result<void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*>::type std::__invoke<void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*>(void (*&&)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*&&, void (*&&)(void*), void*&&) (/home/mgerhardy/dev/oss/engine/build/voxedit/vengi-voxedit+0x878c6e2) (BuildId: aa8b0276cbe3a526c136e6b69891c16b23fd7758)
    #12 0x557aa64c6277 in void std::thread::_Invoker<std::tuple<void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*> >::_M_invoke<0ul, 1ul, 2ul, 3ul>(std::_Index_tuple<0ul, 1ul, 2ul, 3ul>) (/home/mgerhardy/dev/oss/engine/build/voxedit/vengi-voxedit+0x878c277) (BuildId: aa8b0276cbe3a526c136e6b69891c16b23fd7758)
    #13 0x557aa64c5f8b in std::thread::_Invoker<std::tuple<void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*> >::operator()() (/home/mgerhardy/dev/oss/engine/build/voxedit/vengi-voxedit+0x878bf8b) (BuildId: aa8b0276cbe3a526c136e6b69891c16b23fd7758)
    #14 0x557aa64c5f43 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*> > >::_M_run() (/home/mgerhardy/dev/oss/engine/build/voxedit/vengi-voxedit+0x878bf43) (BuildId: aa8b0276cbe3a526c136e6b69891c16b23fd7758)
    #15 0x7f37dc0dee63  (/lib/x86_64-linux-gnu/libstdc++.so.6+0xdee63) (BuildId: 332d8df09457200979fca5abf449dd4629a19f78)
    #16 0x7f37dc65ae65 in asan_thread_start ../../../../src/libsanitizer/asan/asan_interceptors.cpp:234
    #17 0x7f37db6a645b in start_thread nptl/pthread_create.c:444
    #18 0x7f37db726bbb in clone3 ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

0x50f00004c618 is located 72 bytes inside of 176-byte region [0x50f00004c5d0,0x50f00004c680)
freed by thread T0 here:
    #0 0x7f37dc6f2878 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
    #1 0x7f37dc4ba7f2  (/lib/x86_64-linux-gnu/libSDL2-2.0.so.0+0xa57f2) (BuildId: 0fde3d17eb240615a6c99354e493de4c6dea2d72)
    #2 0x557aa4a01157 in ImGui::MemFree(void*) /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui.cpp:4316
    #3 0x557aa64367e5 in void IM_DELETE<ImGuiTestInfoTask>(ImGuiTestInfoTask*) /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui.h:1980
    #4 0x557aa6404aaf in ImGuiTestEngine_PostNewFrame /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_engine.cpp:810
    #5 0x557aa63f95e5 in operator() /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_engine.cpp:162
    #6 0x557aa63f960d in _FUN /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_engine.cpp:162
    #7 0x557aa49f637c in ImGui::CallContextHooks(ImGuiContext*, ImGuiContextHookType) /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui.cpp:3835
    #8 0x557aa4a14087 in ImGui::NewFrame() /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui.cpp:5015
    #9 0x557aa486aebe in ui::IMGUIApp::onRunning() /home/mgerhardy/dev/oss/engine/src/modules/ui/IMGUIApp.cpp:597
    #10 0x557aa3d06951 in VoxEdit::onRunning() /home/mgerhardy/dev/oss/engine/src/tools/voxedit/VoxEdit.cpp:545
    #11 0x557aa65df29c in app::App::onFrame() /home/mgerhardy/dev/oss/engine/src/modules/app/App.cpp:303
    #12 0x557aa65dc3aa in app::App::startMainLoop(int, char**) /home/mgerhardy/dev/oss/engine/src/modules/app/App.cpp:164
    #13 0x557aa3d06ff1 in main /home/mgerhardy/dev/oss/engine/src/tools/voxedit/VoxEdit.cpp:574
    #14 0x7f37db6456c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

previously allocated by thread T10 here:
    #0 0x7f37dc6f3bd7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x7f37dc4ba706  (/lib/x86_64-linux-gnu/libSDL2-2.0.so.0+0xa5706) (BuildId: 0fde3d17eb240615a6c99354e493de4c6dea2d72)
    #2 0x557aa4a00ee3 in ImGui::MemAlloc(unsigned long) /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui.cpp:4300
    #3 0x557aa63fddd5 in ImGuiTestEngine_FindItemInfo(ImGuiTestEngine*, unsigned int, char const*) /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_engine.cpp:450
    #4 0x557aa63b305f in ImGuiTestContext::ItemInfo(ImGuiTestRef, int) /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_context.cpp:992
    #5 0x557aa63d8760 in ImGuiTestContext::ItemDragAndDrop(ImGuiTestRef, ImGuiTestRef, int) /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_context.cpp:3013
    #6 0x557aa3e19e30 in operator() /home/mgerhardy/dev/oss/engine/src/tools/voxedit/modules/voxedit-ui/tests/AssetPanelTest.cpp:27
    #7 0x557aa3e1b03e in __invoke_impl<void, voxedit::AssetPanel::registerUITests(ImGuiTestEngine*, char const*)::<lambda(ImGuiTestContext*)>&, ImGuiTestContext*> /usr/include/c++/13/bits/invoke.h:61
    #8 0x557aa3e1ac49 in __invoke_r<void, voxedit::AssetPanel::registerUITests(ImGuiTestEngine*, char const*)::<lambda(ImGuiTestContext*)>&, ImGuiTestContext*> /usr/include/c++/13/bits/invoke.h:111
    #9 0x557aa3e1a95c in _M_invoke /usr/include/c++/13/bits/std_function.h:290
    #10 0x557aa6439256 in std::function<void (ImGuiTestContext*)>::operator()(ImGuiTestContext*) const (/home/mgerhardy/dev/oss/engine/build/voxedit/vengi-voxedit+0x86ff256) (BuildId: aa8b0276cbe3a526c136e6b69891c16b23fd7758)
    #11 0x557aa64123b3 in ImGuiTestEngine_RunTest(ImGuiTestEngine*, ImGuiTestContext*, ImGuiTest*, int) /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_engine.cpp:1625
    #12 0x557aa64093b4 in ImGuiTestEngine_ProcessTestQueue /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_engine.cpp:1092
    #13 0x557aa6406623 in ImGuiTestEngine_TestQueueCoroutineMain /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_engine.cpp:909
    #14 0x557aa64c171b in CoroutineThreadMain /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_coroutine.cpp:57
    #15 0x557aa64c69f1 in void std::__invoke_impl<void, void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*>(std::__invoke_other, void (*&&)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*&&, void (*&&)(void*), void*&&) /usr/include/c++/13/bits/invoke.h:61
    #16 0x557aa64c66e2 in std::__invoke_result<void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*>::type std::__invoke<void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*>(void (*&&)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*&&, void (*&&)(void*), void*&&) (/home/mgerhardy/dev/oss/engine/build/voxedit/vengi-voxedit+0x878c6e2) (BuildId: aa8b0276cbe3a526c136e6b69891c16b23fd7758)
    #17 0x557aa64c6277 in void std::thread::_Invoker<std::tuple<void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*> >::_M_invoke<0ul, 1ul, 2ul, 3ul>(std::_Index_tuple<0ul, 1ul, 2ul, 3ul>) (/home/mgerhardy/dev/oss/engine/build/voxedit/vengi-voxedit+0x878c277) (BuildId: aa8b0276cbe3a526c136e6b69891c16b23fd7758)
    #18 0x557aa64c5f8b in std::thread::_Invoker<std::tuple<void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*> >::operator()() (/home/mgerhardy/dev/oss/engine/build/voxedit/vengi-voxedit+0x878bf8b) (BuildId: aa8b0276cbe3a526c136e6b69891c16b23fd7758)
    #19 0x557aa64c5f43 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(Coroutine_ImplStdThreadData*, void (*)(void*), void*), Coroutine_ImplStdThreadData*, void (*)(void*), void*> > >::_M_run() (/home/mgerhardy/dev/oss/engine/build/voxedit/vengi-voxedit+0x878bf43) (BuildId: aa8b0276cbe3a526c136e6b69891c16b23fd7758)
    #20 0x7f37dc0dee63  (/lib/x86_64-linux-gnu/libstdc++.so.6+0xdee63) (BuildId: 332d8df09457200979fca5abf449dd4629a19f78)

Thread T10 created by T0 here:
    #0 0x7f37dc6ebaf1 in pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:245
    #1 0x7f37dc0def38 in std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (/lib/x86_64-linux-gnu/libstdc++.so.6+0xdef38) (BuildId: 332d8df09457200979fca5abf449dd4629a19f78)
    #2 0x557aa64c1dba in Coroutine_ImplStdThread_Create /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_coroutine.cpp:77
    #3 0x557aa63fb4e3 in ImGuiTestEngine_Start(ImGuiTestEngine*, ImGuiContext*) /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_engine.cpp:258
    #4 0x557aa4865cea in ui::IMGUIApp::onInit() /home/mgerhardy/dev/oss/engine/src/modules/ui/IMGUIApp.cpp:324
    #5 0x557aa3d04587 in VoxEdit::onInit() /home/mgerhardy/dev/oss/engine/src/tools/voxedit/VoxEdit.cpp:454
    #6 0x557aa65deb42 in app::App::onFrame() /home/mgerhardy/dev/oss/engine/src/modules/app/App.cpp:281
    #7 0x557aa65dc3aa in app::App::startMainLoop(int, char**) /home/mgerhardy/dev/oss/engine/src/modules/app/App.cpp:164
    #8 0x557aa3d06ff1 in main /home/mgerhardy/dev/oss/engine/src/tools/voxedit/VoxEdit.cpp:574
    #9 0x7f37db6456c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: AddressSanitizer: heap-use-after-free /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_context.cpp:3032 in ImGuiTestContext::ItemDragAndDrop(ImGuiTestRef, ImGuiTestRef, int)
Shadow bytes around the buggy address:
  0x50f00004c380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50f00004c400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50f00004c480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50f00004c500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50f00004c580: fa fa fa fa fa fa fa fa fa fa fd fd fd fd fd fd
=>0x50f00004c600: fd fd fd[fd]fd fd fd fd fd fd fd fd fd fd fd fd
  0x50f00004c680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50f00004c700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50f00004c780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50f00004c800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50f00004c880: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==154641==ABORTING
FAILED: src/tools/voxedit/CMakeFiles/voxedit-run /home/mgerhardy/dev/oss/engine/build/src/tools/voxedit/CMakeFiles/voxedit-run 
cd /home/mgerhardy/dev/oss/engine/build/voxedit && /home/mgerhardy/dev/oss/engine/build/voxedit/vengi-voxedit
ninja: build stopped: subcommand failed.
make: *** [Makefile:179: voxedit-run] Fehler 1

vengi-voxel/vengi#454

TestSuite: imgui_app.h for Metal

OF course, "// THIS IS FOR OUR OWN USE AND IS NOT SUPPORTED."
It would be nice to have the struct / types / etc clearly defined in the header rather than in the cpp file.

Please let me know if you'd rather not have me add 'issues' such as this. Alternatively, when I have some free time I can tackle it myself and make a request (lmk)

Define IMGUI_VIDEO_CAPTURE_ENCODER_PATH

It would be nice to have an IMGUI_VIDEO_CAPTURE_ENCODER_PATH definition included in imgui_capture_tool.h much like IMGUI_CAPTURE_DEFAULT_VIDEO_PARAMS[...]. In my world it would be conveniently sourced during ImGuiTestEngine_Postswap;

struct IMGUI_API ImGuiCaptureContext would contain char* VideoCaptureEncoderPath = IMGUI_VIDEO_CAPTURE_ENCODER_PATH or along those lines so we don't have to input it via the UI.

Thoughts?

Minor compiler warning

I don't have capture support enabled and getting this:

/home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_context.cpp:690:13: warning: โ€˜bool ImGuiTestContext_CanCaptureScreenshot(ImGuiTestContext*)โ€™ defined but not used [-Wunused-function]
  690 | static bool ImGuiTestContext_CanCaptureScreenshot(ImGuiTestContext* ctx)

Proposal: Simple MSVC build script (without MSBuild or Make)

Seeking feedback on @ocornut 's thoughts on the below before I commit the time to raising a PR.

I found this project very difficult to build (it took me 2 attempts to get it building from a batch file and the successful attempt took ~5 hours) from the provided instructions for a couple of primary reasons:

  1. I don't use Make or any fancy build system
  2. The vcxproj and solution target the v140 toolset and the Windows 8 SDK, neither of which are installed by default with VS anymore

One of the things I love about Dear ImGui proper is that it doesn't require a huge amount of setup and reading through existing build stuff or install a bunch of pre-requisites to figure out how to get it building.

I was wondering if it is worth me I raising a PR that added a simple build.bat for app_minimal that built using MSVC (basically just using cl.exe)

Is this something that you think would be beneficial?

Error: no member named 'TabBarGetTabName' in namespace 'ImGui'

FYI- Making the latest imgui_test_suite:

c++ -I../ -I../../imgui -I../../imgui/backends -I../libs -g -Wall -Wformat -DIMGUI_TEST_ENGINE_ENABLE_IMPLOT=0 -DIMGUI_USER_CONFIG=\"imgui_test_suite/imgui_test_suite_imconfig.h\" -DIMGUI_APP_SDL_GL3 -D_THREAD_SAFE -I/opt/homebrew/include -I/opt/homebrew/include/SDL2 -std=c++14 -c -o imgui_tests_widgets.o imgui_tests_widgets.cpp
imgui_tests_widgets.cpp:1234:32: error: no member named 'TabBarGetTabName' in namespace 'ImGui'
        IM_CHECK_STR_EQ(ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[0]), "Tab 0");
                        ~~~~~~~^
../imgui_test_engine/imgui_te_context.h:574:69: note: expanded from macro 'IM_CHECK_STR_EQ'
#define IM_CHECK_STR_EQ(_LHS, _RHS)                 IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_None)
                                                                    ^~~~
../imgui_test_engine/imgui_te_context.h:549:100: note: expanded from macro 'IM_CHECK_STR_OP'
        bool __res = ImGuiTestEngine_CheckStrOp(__FILE__, __func__, __LINE__, _FLAGS, #_OP, #_LHS, _LHS, #_RHS, _RHS); \
                                                                                                   ^~~~
imgui_tests_widgets.cpp:1235:32: error: no member named 'TabBarGetTabName' in namespace 'ImGui'
        IM_CHECK_STR_EQ(ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[1]), "Tab 1");
                        ~~~~~~~^
../imgui_test_engine/imgui_te_context.h:574:69: note: expanded from macro 'IM_CHECK_STR_EQ'
#define IM_CHECK_STR_EQ(_LHS, _RHS)                 IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_None)
                                                                    ^~~~
../imgui_test_engine/imgui_te_context.h:549:100: note: expanded from macro 'IM_CHECK_STR_OP'
        bool __res = ImGuiTestEngine_CheckStrOp(__FILE__, __func__, __LINE__, _FLAGS, #_OP, #_LHS, _LHS, #_RHS, _RHS); \
                                                                                                   ^~~~
imgui_tests_widgets.cpp:1236:32: error: no member named 'TabBarGetTabName' in namespace 'ImGui'
        IM_CHECK_STR_EQ(ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[2]), "Tab 2");
                        ~~~~~~~^
../imgui_test_engine/imgui_te_context.h:574:69: note: expanded from macro 'IM_CHECK_STR_EQ'
#define IM_CHECK_STR_EQ(_LHS, _RHS)                 IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_None)
                                                                    ^~~~
../imgui_test_engine/imgui_te_context.h:549:100: note: expanded from macro 'IM_CHECK_STR_OP'
        bool __res = ImGuiTestEngine_CheckStrOp(__FILE__, __func__, __LINE__, _FLAGS, #_OP, #_LHS, _LHS, #_RHS, _RHS); \
                                                                                                   ^~~~
imgui_tests_widgets.cpp:1243:32: error: no member named 'TabBarGetTabName' in namespace 'ImGui'
        IM_CHECK_STR_EQ(ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[0]), "Tab 0");
                        ~~~~~~~^
../imgui_test_engine/imgui_te_context.h:574:69: note: expanded from macro 'IM_CHECK_STR_EQ'
#define IM_CHECK_STR_EQ(_LHS, _RHS)                 IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_None)
                                                                    ^~~~
../imgui_test_engine/imgui_te_context.h:549:100: note: expanded from macro 'IM_CHECK_STR_OP'
        bool __res = ImGuiTestEngine_CheckStrOp(__FILE__, __func__, __LINE__, _FLAGS, #_OP, #_LHS, _LHS, #_RHS, _RHS); \
                                                                                                   ^~~~
imgui_tests_widgets.cpp:1244:32: error: no member named 'TabBarGetTabName' in namespace 'ImGui'
        IM_CHECK_STR_EQ(ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[1]), "Tab 2");
                        ~~~~~~~^
../imgui_test_engine/imgui_te_context.h:574:69: note: expanded from macro 'IM_CHECK_STR_EQ'
#define IM_CHECK_STR_EQ(_LHS, _RHS)                 IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_None)
                                                                    ^~~~
../imgui_test_engine/imgui_te_context.h:549:100: note: expanded from macro 'IM_CHECK_STR_OP'
        bool __res = ImGuiTestEngine_CheckStrOp(__FILE__, __func__, __LINE__, _FLAGS, #_OP, #_LHS, _LHS, #_RHS, _RHS); \
                                                                                                   ^~~~
imgui_tests_widgets.cpp:1249:32: error: no member named 'TabBarGetTabName' in namespace 'ImGui'
        IM_CHECK_STR_EQ(ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[0]), "Tab 0");
                        ~~~~~~~^
../imgui_test_engine/imgui_te_context.h:574:69: note: expanded from macro 'IM_CHECK_STR_EQ'
#define IM_CHECK_STR_EQ(_LHS, _RHS)                 IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_None)
                                                                    ^~~~
../imgui_test_engine/imgui_te_context.h:549:100: note: expanded from macro 'IM_CHECK_STR_OP'
        bool __res = ImGuiTestEngine_CheckStrOp(__FILE__, __func__, __LINE__, _FLAGS, #_OP, #_LHS, _LHS, #_RHS, _RHS); \
                                                                                                   ^~~~
imgui_tests_widgets.cpp:1250:32: error: no member named 'TabBarGetTabName' in namespace 'ImGui'
        IM_CHECK_STR_EQ(ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[1]), "Tab 2");
                        ~~~~~~~^
../imgui_test_engine/imgui_te_context.h:574:69: note: expanded from macro 'IM_CHECK_STR_EQ'
#define IM_CHECK_STR_EQ(_LHS, _RHS)                 IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_None)
                                                                    ^~~~
../imgui_test_engine/imgui_te_context.h:549:100: note: expanded from macro 'IM_CHECK_STR_OP'
        bool __res = ImGuiTestEngine_CheckStrOp(__FILE__, __func__, __LINE__, _FLAGS, #_OP, #_LHS, _LHS, #_RHS, _RHS); \
                                                                                                   ^~~~
imgui_tests_widgets.cpp:1251:32: error: no member named 'TabBarGetTabName' in namespace 'ImGui'
        IM_CHECK_STR_EQ(ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[2]), "Tab 1");

ImGuiItemStatusFlags errors when building

I'm trying to set this up to test https://github.com/santaclose/ste
I tried to follow this guide: https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up

Add to your imconfig file #include "imgui_te_imconfig.h or add equivalent configuration directives.

I'm not sure what you mean by "your imconfig file" and I don't think I have one, but I tried to add the configuration to one of my cpp files:
image

And don't know where the ImGuiItemStatusFlags identifiers are supposed to be defined ๐Ÿค”
image

Thanks in advance.

Conan package

Hello,

It would be very nice to have Conan package for imgui_test_engine as there already exists package for imgui itself.

ImGui Conan package issue (ocornut/imgui#1469)
ImGui package in Conan center (https://conan.io/center/recipes/imgui)
ImGui package recipe (https://github.com/conan-io/conan-center-index/tree/master/recipes/imgui)

I find Conan very convenient tool for managing library dependencies. Fresh checkout can be easily build from scratch in fresh environment. Only requirements are that Conan + build tools are installed. Conan will automatically resolve package dependencies and load required packages into local machine.

Below are code pieces from my project using Conan+CMake for adding ImGui into project:
`
conanfile.py
def requirements(self):
self.requires("imgui/cci.20230105+1.89.2.docking")

CMakeLists.txt
# find libraries
find_package(imgui QUIET CONFIG)
# link libraries
target_link_libraries(${TARGET} PRIVATE imgui::imgui)

main.cpp
#include "imgui.h"
ImGui::CreateContext();
`

Would you consider having an option to disable the coroutine?

As titled, in my case, my motivation is essentially to have the code follow deterministic pattern for our Kotlin port

However, if this may make sense for other scenarios as well, then it might be worth implementing it

Ps: sorry if I opened an issue, but there is no discussion available

CaptureScreenshotWindow("...", ImGuiCaptureFlags_StitchAll ) fails

Re-Bonjour Omar,

Sorry if I'm flooding the issues a bit today.

I'm trying the code of app_minimal, and I have an issue with screen capture and the flag ImGuiCaptureFlags_StitchAll.

Basically,

  • I'm getting a fully black capture under windows (although it has the correct size). This is strange. I'm using Windows 10 ARM 64 bits (although I compiled the code in x64 mode)
  • under macOS (after having applied a hack to take into account the FrameBufferScale), the scroll does not work, and I get a repeated image:
image

i.e. the size is correct, but it seems like the content is repeated (as if the scroll did not work).
Based on the usleep(1000) for linux, I tried to add a sleep(1 second) during the capture, and I could see that effectively the scroll does not work.

GatherItems can't see through childs and tables

Hi!
I am automating tests on an app using ImGui for its UI, and I am running into trouble with exploring childs, and more importantly tables.
As the title states: GatherItems doesn't seem to be able to find items inside childs (BeginChild/EndChild) or tables BeginTable/EndTable), while it will find items inside things like tree nodes.

Here is a minimalist-ish test case.

    auto test = IM_REGISTER_TEST(engine, "tests", "gather childs");
    test->GuiFunc = [](ImGuiTestContext*) {
        if(ImGui::Begin("Window")) {
            if(ImGui::BeginChild("Child")) {
                ImGui::Button("Button1");
                ImGui::Button("Button2");
                ImGui::Button("Button3");
                ImGui::Button("Button4");
            }
            ImGui::EndChild();
            ImGui::End();
        }
    };
   test->TestFunc = [](ImGuiTestContext* ctx) {
        IM_CHECK_NE(ctx->ItemInfoOpenFullPath("//Window")->ID, 0);             // Ok
        IM_CHECK_NE(ctx->ItemInfoOpenFullPath("//Window/**/Button2")->ID, 0);  // Ok

        ImGuiTestItemList items;
        ctx->GatherItems(&items, "//Window");

        std::printf("%llu items\n", items.size());                             //  Only 2 items found, not sure what they are, but they do not match any of the expected items
        for(const ImGuiTestItemInfo& info : items) {               
            std::printf("    %x: '%s'\n", info.ID, info.DebugLabel);
        }

        IM_CHECK_GT(items.size(), 2);                                          // Fails: items.size() is 2
    };

Provide Windows implementation for function ImFileExist

image
Hello, right now this function shown in the picture above does not compile on Windows. An implementation should be provided that would compile on Windows. This function (ImFileExist) is defined in file imgui_te_utils.cpp.

Idea: testing language bindings by comparing "screen recordings" of imgui_demo

Hi!

I maintain a bindings project for another programming language. I always cover the whole API and port over the whole imgui_demo.

I don't have any tests, I just sometimes check if the reimplementation of the demo still works as expected, by poking around it manually. If something doesn't look right, I can't be sure whether the bug is in the bindings themselves or in my port of the demo, but either way it's a bug for me to solve. To make this slightly more convenient, I also have the original C++-based demo window opened right next to it, for comparison.

So my testing session might look like this (click to see screenshot)

screenshot

Manual testing is not sustainable at all, though.


So the main idea people might think of instead is to also bind "imgui_test_engine" to the destination language and port "imgui_test_suite" to it. But that's waaay too much work. Let's think outside the box โ€“ maybe we can somehow just automate my manual testing steps?


Having a reimplementation of imgui_demo (the crucial part) alongside the C++ original imgui_demo can actually open an interesting and kinda lazy approach for making a test โ€“ we'll just check that the reimplementation behaves identically to the original. Specifically, make several tests like this:

  • Click around and actually record the process โ€“ all the inputs.
  • Replay the exact same clicks both on the original C++ demo and on the bindings-reimplemented demo window, while recording what gets rendered on the screen.
  • Assert that the two rendered recordings are exactly the same.

But when thinking about this historically, I was stopped by several challenges โ€“ which actually all seem to have a direct solution in this repository.

  1. I don't know what exactly would be a good way to record all inputs on a timeline, as well as record graphical outputs in a way that's not wasteful and not affected by timing artifacts.

    • From my initial understanding, "imgui_test_engine" has already solved this challenge as part of its implementation, so that's great.
  2. My handmade recordings of clicks might become obsoleted by changes to Dear ImGui (even any minor design changes) and actually also changes to imgui_demo itself. So this is not future-proof at all.

    • Luckily seems like another thing that "imgui_test_engine" can do is synthesize a series of inputs based on declarations of "what to do". So, great, I could just write the declarations instead, then record the synthesized inputs (or even synthesize on the fly, of course). At least I assume that this repository can be reused to synthesize and replay inputs on another arbitrary window.
  3. Maybe I'm too lazy to actually write declarative tests or think of interesting test cases. Can't I just take them from someone who already wrote them?

    • Well... this repository is the real motherload, then! Even though many of the tests first create their own window to then interact with (meaning I'd have to port at least all the t->GuiFunc method bodies), a large chunk of tests simply reuses the imgui_demo window, which I already have ported! So with this single requirement, all these tests (filtered as ./imgui_test_suite demo, as I understand) can be reused directly!
  4. Oh but surely porting all those asserts that are written in C++ is too much work?

    • That's a trick question! No, we'll just ignore the asserts and only use the "screen recording". If the bindings-based demo produces an identical recording, then surely it would've passed the asserts as well.

In summary, if I just somehow wire all this machinery together, I should be able to get a large test suite for correctness of language bindings without writing a single test method body myself.

For now I'm just sharing this idea here, I hope this can inspire people, and eventually I'll probably ask for some assistance with understanding this repository and picking it apart ๐Ÿ˜…
Also I hope I'm not saying something that's already widely known. Or if that's the case, great, point me that way!

Is it possible to use this Test engine for integration tests?

Does the backend or something matter for ImGui to work in this case? I want to somehow run Imgui tests using Google test but in an integration way where the real application is not running but I am calling Imgui to render the UI and then automatically clicking buttons etc and checking the results.

Segfault in ImGuiTestContext::ComboClick

const char* p = ImStrchrRangeWithEscaping(path, path_end, '/');

might return null and maybe a IM_CHECK_XXX call should get added to inform the user that the input was wrong.

#5  0x000055555cd0e6b5 in ImGuiTestContext::ComboClick (this=0x7fffced84750, ref=...) at /home/mgerhardy/dev/oss/engine/src/modules/ui/dearimgui/imgui_test_engine/imgui_te_context.cpp:3303
3303	    Str128f combo_item_buf = Str128f("//%s/**/%s", popup->Name, p + 1);
(gdb) p popup
$1 = (ImGuiWindow *) 0x51900005c880
(gdb) p popup->Name
$2 = 0x50200012c050 "##Combo_00"
(gdb) p p
$3 = 0x0

How to query value from ref?

How do I check the value of something by querying its ref?
I'm hoping to do something like this:

ctx->ItemInputValue("sVal", 5.5f); // sets the sVal DragFloat to 5.5f
IM_CHECK_EQ("sVal", 5.5f); // Checks that the value has been set correctly

I'm not using GuiFunc, so I don't 'think' using GetVars is what I need, and I don't have the variable tied to an app class or anything.

It's being used more like:

    ImGui::NewFrame();
    {
      ImGui::Begin("My Window", NULL, window_flags);
      static float sVal = 0.0f;
      ImGui::DragFloat("sVal", &sVal);`
      ImGui::End();
    }

I just want to verify that the slider value is set correctly.

Unmangle child ids

It looks like I really can't get this running.. The item ##_top_selector_neo is not found

I have the following path copied from Stack Tool

// /Animation###animationtimeline\/##neo-sequencer-child_EEF00089\/####neo-sequencer_child_wrapper_D29EAF74/##neo-sequencer/##_top_selector_neo
ctx->SetRef("Animation###animationtimeline");
ctx->SetRef(ctx->WindowInfo("##neo-sequencer-child")->ID);
ctx->SetRef(ctx->WindowInfo("####neo-sequencer_child_wrapper")->ID);
ctx->MouseMove("##neo-sequencer/##_top_selector_neo");
ctx->MouseDragWithDelta({10.0f, 0.0f});
ctx->ItemClick("###Add");

These are the logs

[0000] Test: 'animationtimeline' 'create keyframe'..
[0000] -- SetInputMode 1
[0001] -- ItemClick '###viewport0' > 00000000
[0016] -- WindowFocus('22585415 > 'NULL'')
[0017] -- SetRef '๎…ฟ Animation###animationtimeline' 22585415
[0017] OK AnimationTimelineTest.cpp:20 'focusWindow(ctx, title)'
[0017] -- WindowInfo: by path: '##neo-sequencer-child'
[0018] -- SetRef 'NULL' 4226E3F2
[0018] -- WindowInfo: by path: '####neo-sequencer_child_wrapper'
[0019] -- SetRef 'NULL' AAFE48E8
[0023] Error 'Unable to locate item: '/##neo-sequencer/##_top_selector_neo''
[0023] -- MouseMove to '##neo-sequencer/##_top_selector_neo' > 00000000
[0023] create keyframe test failed.

side note - the mentioned focusWindow function in the logs looks like this

bool Panel::focusWindow(ImGuiTestContext *ctx, const char *title) {
	IM_CHECK_SILENT_RETV(title != nullptr, false);
	ImGuiWindow* window = ImGui::FindWindowByName(title);
	if (window == nullptr) {
		ctx->LogError("Error: could not find window with title/id %s", title);
		IM_CHECK_SILENT_RETV(window != nullptr, false);
	}
	ctx->WindowFocus(window->ID);
	ctx->SetRef(window);
	return true;
}

As a sidenote - I've also tried to use //**/##neo-sequencer-child and //**/**/##neo-sequencer-child (up to 5 times **/ out of ignorance ;) )

And last but not least a screenshot of the "problem" ;)

Bildschirmfoto vom 2024-04-20 18-03-41

Testing click and drag from a pop up

I've run into an issue when testing clicking and dragging from a pop up window. I can see what's happening:

  1. I ItemClick() the button that opens the popup.
  2. I then use //$FOCUSED to get the pop up path to the button that I want to drag with ItemDragAndDrop()
  3. Inside the ItemDragAndDrop it successfully finds the src and dst ref but then it calls WindowBringToFront(item_dst->Window->ID); which switches to the dst window.
  4. Then it does MouseMove(ref_src, ImGuiTestOpFlags_NoCheckHoveredId); but now $FOCUSED points has the dst path instead so the src_ref isn't found anymore.

It seems to give pop up windows a generated path like "##Popup_438a024f" rather then the actual id for the pop up window so I'm not sure how to get a stable path to the item that I want to drag. I tried a wildcard too but couldn't get it to work with the pop up either (could be doing it wrong though, will keep testing.

Absolutely love this test engine though, will make life so much easier to test for bugs before each release, thanks for the hard work!

Can we get ImGuiTestEngine_UnregisterTest, too

The use-case I have is that my UI creates or removes widgets when I switch behaviour (I have a simple and a more complex mode). The widgets register their ui unittests on spawning, but I can't unregister them on removing the widget and that leads to a crash in the test engine.

ImGuiTestOpFlags_NoFocusWindow flag ignored in function WindowBringToFront

image
Hello, I have this test and by running it I got suspicious that maybe the function WindowBringToFront is not working correctly.

The test works like this, at some point the engine clicks the button named "Launch", the window called "QAP" is focused. Because "Launch" has been clicked, another window opens called "Overlay", it is created with flag ImGuiWindowFlags_NoFocusOnAppearing so it is hidden behind window "QAP". In the test then I want to bring it to front without focusing it so I am calling the function WindowBringToFront with flag ImGuiTestOpFlags_NoFocusWindow. But the function focuses the "Overlay" window anyway.

image
I checked the implementation of the WindowBringToFront function. I saw that it does not check if the flag ImGuiTestOpFlags_NoFocusWindow is set. So as it can be seen in the picture I added the check and then the test worked as expected.

Minor issue in "misc_clipper"

        for (int clipper_step = 0; clipper_step < 2; clipper_step++)
            for (int step = 0; step < step_count; step++)
            {
                vars.ClipperManualItemHeight = (clipper_step == 2);

clipper_step never reaches 2, that means vars.ClipperManualItemHeight is set always to false

How to get context of items that are in a 3D scene to participate in a test?

<please, if there is an FAQ or other place to ask these questions do let me know>

Thus far ImGui Test Engine checks off most of everything I require from a test framework. What I really would like to know is how (if at all possible) to register callbacks from items that I select within the context of the 3D scene that I have in my app such that I can use them within the test framework?

For example, if my little app has a few objects, and I select one and some behaviour happens, what might be the most sensible way for me to register the event and callback such that later, within that same scene, I can write a test that automates the given action? Is that even sensible within ImGui Test Engine?

CaptureScreenshotWindow on docked window leads to a black capture

Hello Omar,

Since the capture hides all other windows, the parent window of a docked window will be hidden and as a result, the screenshot will be black.

There is no emergency. I do not need that at the moment.

Steps to reproduce

Add and call this inside app_minimal_main.cpp

void ShowDockedWindows()
{
    // Create parent DockSpace inside a window
    auto dockSpaceId =  ImGui::GetID("ParentDockSpace");
    ImGui::SetNextWindowSize(ImVec2(400.f, 400.f));
    ImGui::Begin("ParentDockWindow");
    ImGui::DockSpace(dockSpaceId, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode);

    // Create two initially docked windows

    ImGui::SetNextWindowDockID(dockSpaceId, ImGuiCond_Appearing);
    ImGui::Begin("Window1");
    ImGui::Text("Window1");
    ImGui::End();

    ImGui::SetNextWindowDockID(dockSpaceId, ImGuiCond_Appearing);
    ImGui::Begin("Window2");
    ImGui::Text("Window2");
    ImGui::End();

    // End DockSpace's window
    ImGui::End();
}

Add this inside app_minimal_tests.cpp:

    t = IM_REGISTER_TEST(e, "demo_tests", "capture_docked_window");
    t->TestFunc = [](ImGuiTestContext* ctx)
    {
        ctx->ItemClick("Window1");
        ctx->CaptureScreenshotWindow("Window1");
    };

Attempted fix

I tried the following fix, but it is not enough:

imgui_te_context.cpp

bool ImGuiTestContext::CaptureAddWindow(ImGuiTestRef ref)
{
    ImGuiWindow* window = GetWindowByRef(ref);

    IM_CHECK_SILENT_RETV(window != NULL, false);
    CaptureArgs->InCaptureWindows.push_back(window);

    // Try to keep parent window visible if the window is docked: this is not enough
    ImGuiDockNode* dockNode = window->DockNode;
    if (dockNode != nullptr)
        CaptureArgs->InCaptureWindows.push_back(dockNode->HostWindow);

    return true;
}

Screen capture on macOS with HighDPI fails because of FrameBufferScale!=1

Bonjour Omar,

Under macOS with HighDpi, ImGui::GetDrawData().FrameBufferScale is equal to (2., 2.)

The window coordinates are correctly shown by the capture tool, however when capturing we get a capture with bad coordinates

edges shown by capture tool
image

capture that I get
image

This is due to the fact that the FramebufferScale is not 1.

This is probably related to #8

See the current capture implementation in imgui_app.cpp: it cannot handle the scale

#if defined(IMGUI_APP_GL2) || defined(IMGUI_APP_GL3)
static bool ImGuiApp_ImplGL_CaptureFramebuffer(ImGuiApp* app, int x, int y, int w, int h, unsigned int* pixels, void* user_data)
{
    IM_UNUSED(app);
    IM_UNUSED(user_data);

#ifdef __linux__
    // FIXME: Odd timing issue is observed on linux (Plasma/X11 specifically), which causes outdated frames to be captured, unless we give compositor some time to update screen.
    // glFlush() didn't seem enough. Will probably need to revisit that.
    usleep(1000);   // 1ms
#endif

    int y2 = (int)ImGui::GetIO().DisplaySize.y - (y + h);
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glReadPixels(x, y2, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

    // Flip vertically
    size_t comp = 4;
    size_t stride = (size_t)w * comp;
    unsigned char* line_tmp = new unsigned char[stride];
    unsigned char* line_a = (unsigned char*)pixels;
    unsigned char* line_b = (unsigned char*)pixels + (stride * ((size_t)h - 1));
    while (line_a < line_b)
    {
        memcpy(line_tmp, line_a, stride);
        memcpy(line_a, line_b, stride);
        memcpy(line_b, line_tmp, stride);
        line_a += stride;
        line_b -= stride;
    }
    delete[] line_tmp;
    return true;
}
#endif

I previously implemented a screen capture utility in HelloImGui, and I had to take the FrameBufferScale into account:
see opengl_screenshot.cpp in the hello imgui code.

Basically, it multiplies the capture size, like this:

        auto draw_data = ImGui::GetDrawData();
        int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
        int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);

However this solution cannot apply here for two main reasons:

  • the pixel buffer is preallocated, and thus cannot fit the OpenGL capture (which will be 4 times as big as the allocated buffer, since width and height are twice bigger)
  • when inside ImGuiApp_ImplGL_CaptureFramebuffer, GetDrawData() may return null so that we cannot reliably access the FrameBufferScale

I tried a dirty hack that consists of creating an intermediary buffer for the capture, and then do a simple resize. It can work, but it is not very nice, and require to allocate an intermediary buffer.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.