-
Notifications
You must be signed in to change notification settings - Fork 53
Automation API
Once the Test Engine is setup, most interactions are done via the ImGuiTextContext
interface.
Refer to imgui_te_context.h for API and comments.
You may refer to app_minimal_tests.cpp as a reference for basic tests. You may refer to our test suite imgui_test_suite/ for a larger amount of reference code. Please note that our test suite is written primarily with the intent of testing Dear ImGui, not with the intent of providing examples for using the API.
Tests are registered via the IM_REGISTER_TEST()
macro.
IM_REGISTER_TEST(ImGuiTestEngine* engine, const char* test_category, const char* test_name);
(This internally uses ImGuiTestEngine_RegisterTest()
but we use a macro so we can record the source file/line of tests, making it convenient in the interactive UI to preview test source code. Please always use the macro!)
Registering an example test:
t = IM_REGISTER_TEST(e, "demo_tests", "test1");
t->GuiFunc = [](ImGuiTestContext* ctx) // Optionally provide a GUI function in addition to your application GUI
{
ImGui::Begin("Test Window", NULL, ImGuiWindowFlags_NoSavedSettings);
ImGui::Text("Hello, automation world");
ImGui::Button("Click Me");
if (ImGui::TreeNode("Node"))
{
static bool b = false;
ImGui::Checkbox("Checkbox", &b);
ImGui::TreePop();
}
ImGui::End();
};
t->TestFunc = [](ImGuiTestContext* ctx) // Generally provide a Test function which will drive the test.
{
ctx->SetRef("Test Window");
ctx->ItemClick("Click Me");
ctx->ItemOpen("Node"); // Optional as ItemCheck("Node/Checkbox") can do it automatically
ctx->ItemCheck("Node/Checkbox");
ctx->ItemUncheck("Node/Checkbox");
};
- A failed check will:
- Output an error and set the test status accordingly.
- Optionally break in debugger (if
test_io.ConfigBreakOnError
is set). - Return from the function (unless
_NORET()
is used).
- The main macro is
IM_CHECK()
, e.g.IM_CHECK(result == true)
; - Operator macros are preferred as they will display the VALUE of both operands in the log:
IM_CHECK_EQ()
,IM_CHECK_NE()
,IM_CHECK_LT()
,IM_CHECK_LE()
,IM_CHECK_GT()
,IM_CHECK_GE()
. - Add suffix
_NORET
e.g.IM_CHECK_NO_RET(result == true);
to prevent the macro from returning from function on failure (which is the default). - Add suffix
_RETV
e.g.IM_CHECK_RETV(result == true, -1);
to return a specific error is running from sub-functions. - Add suffix
_SILENT
e.g.IM_CHECK_SILENT(window != NULL);
to omit appending to log on lower verbose levels (reduce spam). - Specialized checks for strings:
IM_CHECK_STR_EQ()
,IM_CHECK_STR_NE()
. - Specialized checks for floating point values:
IM_CHECK_FLOAT_EQ_EPS()
,IM_CHECK_FLOAT_NEAR()
.
t = IM_REGISTER_TEST(e, "demo_tests", "always_fail");
t->TestFunc = [](ImGuiTestContext* ctx)
{
int value = 1;
IM_CHECK_EQ(value, 2);
};
(1)
One solution is to set the void* UserData
field of the structure ImGuiTest
:
t = IM_REGISTER_TEST(e, "myapp_tests", "test1");
t->UserData = (void*)my_app; // Associate application pointer when registering
t->TestFunc = [](ImGuiTestContext* ctx)
{
MyApp* my_app = (MyApp*)ctx->Test->UserData; // Retrieve application pointer when running.
[....]
// access your data however you need, e.g.
IM_CHECK_EQ(my_app.GetSettings()->GridEnabled, true);
};
(2) Depending on the nature of your tests, you may want to share variables between your application or your GuiFunc and your TestFunc. This is mostly only ever useful if you are using a GuiFunc (e.g. for testing your own widgets or dialogs independently from your main application). To do this, you can request the test engine to instantiate a custom structure during the duration of a test running:
struct TestVars { int MyInt = 42; };
t = IM_REGISTER_TEST(e, "demo_tests", "test2");
t->SetVarsDataType<TestVars2>();
t->GuiFunc = [](ImGuiTestContext* ctx)
{
auto& vars = ctx->GetVars<TestVars>();
ImGui::Begin("Test Window", NULL, ImGuiWindowFlags_NoSavedSettings);
ImGui::SliderInt("Slider", &vars.MyInt, 0, 1000);
ImGui::End();
};
t->TestFunc = [](ImGuiTestContext* ctx)
{
auto& vars = ctx->GetVars<auto>();
ctx->SetRef("Test Window");
IM_CHECK_EQ(vars.MyInt, 42); // Check default value
ctx->ItemInputValue("Slider", 123);
IM_CHECK_EQ(vars.MyInt, 123); // Check value after our action
};
As a convenience, we also provide a structure called ImGuiTestGenericVars
and always available in ctx->GenericVars
. The structure hold variety of ready-to-use named fields.
(3)
You can access common widget states via ctx->ItemIsChecked("my_checkbox")
, ctx->ItemIsOpened("my_tree_node")
and other status flags emitted by ctx->ItemInfo()
. Some helpers such as ctx->ItemSelectAndReadValue()
can extract data by selecting it and copying it into clipboard.
(4) We are working on a "screen reader" tool to read text and shapes emitted by widgets, so a future possibility would be to read data from the visual output.
See Named References page where all nice uses of ImGuiTestRef
are explained:
ctx->SetRef("Window"); // Set Base Reference
ctx->ItemClick("Button"); // Relative path
ctx->ItemClick("Node/Item"); // Relative path (composite)
ctx->ItemClick("//Window/Button"); // Absolute path
ctx->ItemClick("//$FOCUSED/Button"); // Relative to focused window
ctx->ItemClick("//Window/List/$$0/Button"); // Encode literal integer in string, for PushID(int)
ctx->ItemClick("//Window/**/Button"); // Wildcard search by full label
ctx->WindowInfo("//Window/Child/SubChild"); // Find child window by short name
Refer to imgui_te_context.h
(TODO)