Skip to content

Commit c7f43b2

Browse files
committed
Add backend preferences regression test
Add a focused regression test and supporting plumbing for the renderer backend selector. A new test script (src/imiv/tools/imiv_backend_preferences_regression.py) exercises the Preferences backend combo and verifies requested/next_launch/restart semantics; CMake registers a ctest (imiv_backend_preferences_regression) when a build contains multiple compiled backends. Test-engine and state improvements: include backend state in viewer-state JSON (active, requested, next_launch, restart_required, compiled/not_compiled), add compiled_backend_count() and header exposure, and pass active_backend into test-engine JSON writer. Enhance the test engine with register_test_engine_item_label and XML attributes (set_ref, item_click) to support targeted interactions. Other changes: Preferences UI registers item labels for testing, main window title now shows OIIO version, README and multi-backend docs updated, and a test image asset added.
1 parent 976ea19 commit c7f43b2

13 files changed

Lines changed: 683 additions & 3 deletions

src/imiv/CMakeLists.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,20 @@ if (_imiv_selected_renderer STREQUAL "")
731731
endif ()
732732
endif ()
733733

734+
set (_imiv_enabled_backend_count 0)
735+
if (_imiv_enabled_vulkan)
736+
math (EXPR _imiv_enabled_backend_count
737+
"${_imiv_enabled_backend_count} + 1")
738+
endif ()
739+
if (_imiv_enabled_metal)
740+
math (EXPR _imiv_enabled_backend_count
741+
"${_imiv_enabled_backend_count} + 1")
742+
endif ()
743+
if (_imiv_enabled_opengl)
744+
math (EXPR _imiv_enabled_backend_count
745+
"${_imiv_enabled_backend_count} + 1")
746+
endif ()
747+
734748
set (_imiv_renderer_is_vulkan OFF)
735749
set (_imiv_renderer_is_metal OFF)
736750
set (_imiv_renderer_is_opengl OFF)
@@ -1318,6 +1332,25 @@ if (TARGET imiv
13181332
LABELS "imiv;gui"
13191333
TIMEOUT 360)
13201334

1335+
if (_imiv_enabled_backend_count GREATER 1)
1336+
add_test (
1337+
NAME imiv_backend_preferences_regression
1338+
COMMAND
1339+
"${Python3_EXECUTABLE}"
1340+
"${CMAKE_CURRENT_SOURCE_DIR}/tools/imiv_backend_preferences_regression.py"
1341+
--bin "$<TARGET_FILE:imiv>"
1342+
--cwd "$<TARGET_FILE_DIR:imiv>"
1343+
${_imiv_ctest_default_backend_args}
1344+
--env-script "${CMAKE_BINARY_DIR}/imiv_env.sh"
1345+
--out-dir "${CMAKE_BINARY_DIR}/imiv_captures/backend_preferences_regression"
1346+
--open "${PROJECT_SOURCE_DIR}/ASWF/logos/openimageio-stacked-gradient.png")
1347+
set_tests_properties (
1348+
imiv_backend_preferences_regression PROPERTIES
1349+
LABELS "imiv;gui;imiv_backend"
1350+
TIMEOUT 180
1351+
SKIP_RETURN_CODE 77)
1352+
endif ()
1353+
13211354
if (OIIO_IMIV_ADD_BACKEND_VERIFY_CTEST)
13221355
if (_imiv_enabled_vulkan)
13231356
add_test (

src/imiv/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ cmake -S . -B build_u \
108108
ctest --test-dir build_u -N | rg imiv_backend_verify
109109
```
110110

111+
Focused backend-selector regression from a multi-backend build:
112+
113+
```bash
114+
ctest --test-dir build_u -V -R '^imiv_backend_preferences_regression$'
115+
```
116+
111117
Main output logs:
112118

113119
- `verify_smoke.log`

src/imiv/imiv_app.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <OpenImageIO/imagebuf.h>
4343
#include <OpenImageIO/imagecache.h>
4444
#include <OpenImageIO/imageio.h>
45+
#include <OpenImageIO/oiioversion.h>
4546
#include <OpenImageIO/strutil.h>
4647
#include <OpenImageIO/sysutil.h>
4748

@@ -148,6 +149,12 @@ namespace {
148149
return config.requested_backend;
149150
return sanitize_backend_kind(ui_state.renderer_backend);
150151
}
152+
153+
std::string build_main_window_title(BackendKind active_backend)
154+
{
155+
return Strutil::fmt::format("ImIv v.{} [{}]", OIIO_VERSION_STRING,
156+
backend_cli_name(active_backend));
157+
}
151158
} // namespace
152159

153160

@@ -223,8 +230,9 @@ run(const AppConfig& config)
223230
return EXIT_FAILURE;
224231
}
225232

233+
const std::string window_title = build_main_window_title(active_backend);
226234
GLFWwindow* window = platform_glfw_create_main_window(
227-
active_backend, 1600, 900, "imiv", startup_error);
235+
active_backend, 1600, 900, window_title.c_str(), startup_error);
228236
if (window == nullptr) {
229237
print(stderr, "imiv: {}\n", startup_error);
230238
platform_glfw_terminate();
@@ -494,7 +502,8 @@ run(const AppConfig& config)
494502
#endif
495503

496504
#if defined(IMGUI_ENABLE_TEST_ENGINE)
497-
ViewerStateJsonWriteContext test_engine_state_ctx = { &viewer, &ui_state };
505+
ViewerStateJsonWriteContext test_engine_state_ctx = { &viewer, &ui_state,
506+
active_backend };
498507
TestEngineHooks test_engine_hooks;
499508
test_engine_hooks.image_window_title = image_window_title();
500509
test_engine_hooks.screen_capture = renderer_screen_capture;

src/imiv/imiv_aux_windows.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ draw_preferences_window(PlaceholderUiState& ui, bool& show_window,
291291
backend_label += " (not built)";
292292
}
293293
if (ImGui::BeginCombo("Renderer backend", backend_label.c_str())) {
294+
register_test_engine_item_label("Renderer backend", true);
294295
const bool auto_selected = requested_backend == BackendKind::Auto;
295296
if (ImGui::Selectable("Auto", auto_selected)) {
296297
ui.renderer_backend = static_cast<int>(BackendKind::Auto);
@@ -312,6 +313,8 @@ draw_preferences_window(PlaceholderUiState& ui, bool& show_window,
312313
ImGui::SetItemDefaultFocus();
313314
}
314315
ImGui::EndCombo();
316+
} else {
317+
register_test_engine_item_label("Renderer backend", true);
315318
}
316319
ImGui::TextUnformatted("Current backend");
317320
ImGui::SameLine();

src/imiv/imiv_backend.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,15 @@ compiled_backend_info()
186186
return info;
187187
}
188188

189+
size_t
190+
compiled_backend_count()
191+
{
192+
size_t count = 0;
193+
for (const BackendInfo& info : compiled_backend_info()) {
194+
if (info.compiled)
195+
++count;
196+
}
197+
return count;
198+
}
199+
189200
} // namespace Imiv

src/imiv/imiv_backend.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,7 @@ bool
4040
backend_kind_is_compiled(BackendKind kind);
4141
const std::array<BackendInfo, 3>&
4242
compiled_backend_info();
43+
size_t
44+
compiled_backend_count();
4345

4446
} // namespace Imiv

src/imiv/imiv_frame.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "imiv_frame.h"
66

77
#include "imiv_actions.h"
8+
#include "imiv_backend.h"
89
#include "imiv_drag_drop.h"
910
#include "imiv_image_view.h"
1011
#include "imiv_menu.h"
@@ -322,6 +323,49 @@ test_engine_json_write_ocio_state(FILE* f, const PlaceholderUiState& ui_state)
322323
std::fputs("\n }\n }", f);
323324
}
324325

326+
void
327+
test_engine_json_write_backend_state(FILE* f, const PlaceholderUiState& ui_state,
328+
BackendKind active_backend)
329+
{
330+
const BackendKind requested_backend = sanitize_backend_kind(
331+
ui_state.renderer_backend);
332+
const BackendKind next_launch_backend = resolve_backend_request(
333+
requested_backend);
334+
const bool requested_backend_compiled
335+
= requested_backend == BackendKind::Auto
336+
|| backend_kind_is_compiled(requested_backend);
337+
338+
std::vector<std::string> compiled_backends;
339+
std::vector<std::string> unavailable_backends;
340+
compiled_backends.reserve(compiled_backend_info().size());
341+
unavailable_backends.reserve(compiled_backend_info().size());
342+
for (const BackendInfo& info : compiled_backend_info()) {
343+
if (info.compiled)
344+
compiled_backends.emplace_back(info.cli_name);
345+
else
346+
unavailable_backends.emplace_back(info.cli_name);
347+
}
348+
349+
std::fputs(",\n \"backend\": {\n", f);
350+
std::fputs(" \"active\": ", f);
351+
test_engine_json_write_escaped(f, backend_cli_name(active_backend));
352+
std::fputs(",\n \"active_runtime\": ", f);
353+
test_engine_json_write_escaped(f, backend_runtime_name(active_backend));
354+
std::fputs(",\n \"requested\": ", f);
355+
test_engine_json_write_escaped(f, backend_cli_name(requested_backend));
356+
std::fputs(",\n \"next_launch\": ", f);
357+
test_engine_json_write_escaped(f, backend_cli_name(next_launch_backend));
358+
std::fputs(",\n \"requested_compiled\": ", f);
359+
std::fputs(requested_backend_compiled ? "true" : "false", f);
360+
std::fputs(",\n \"restart_required\": ", f);
361+
std::fputs(next_launch_backend != active_backend ? "true" : "false", f);
362+
std::fputs(",\n \"compiled\": ", f);
363+
test_engine_json_write_string_array(f, compiled_backends);
364+
std::fputs(",\n \"not_compiled\": ", f);
365+
test_engine_json_write_string_array(f, unavailable_backends);
366+
std::fputs("\n }", f);
367+
}
368+
325369
bool
326370
write_test_engine_viewer_state_json(const std::filesystem::path& out_path,
327371
void* user_data, std::string& error_message)
@@ -407,6 +451,7 @@ write_test_engine_viewer_state_json(const std::filesystem::path& out_path,
407451
test_engine_json_write_escaped(f, viewer.area_probe_lines[i].c_str());
408452
}
409453
std::fputs("]", f);
454+
test_engine_json_write_backend_state(f, ui_state, ctx->active_backend);
410455
test_engine_json_write_ocio_state(f, ui_state);
411456
std::fputs("\n}\n", f);
412457
std::fflush(f);

src/imiv/imiv_frame.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#pragma once
66

7+
#include "imiv_backend.h"
78
#include "imiv_renderer.h"
89
#include "imiv_ui.h"
910

@@ -33,6 +34,7 @@ struct DeveloperUiState {
3334
struct ViewerStateJsonWriteContext {
3435
const ViewerState* viewer = nullptr;
3536
const PlaceholderUiState* ui_state = nullptr;
37+
BackendKind active_backend = BackendKind::Auto;
3638
};
3739

3840
bool

src/imiv/imiv_test_engine.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ namespace {
131131
int post_action_delay_frames = 0;
132132
bool has_key_chord = false;
133133
ImGuiKeyChord key_chord = 0;
134+
std::string set_ref;
135+
std::string item_click_ref;
134136
TestEngineMouseTargetMode mouse_target_mode
135137
= TestEngineMouseTargetMode::None;
136138
float mouse_x = 0.0f;
@@ -298,6 +300,15 @@ namespace {
298300
step.action.has_key_chord = true;
299301
}
300302

303+
const pugi::xml_attribute item_click_attr = step_node.attribute(
304+
"item_click");
305+
if (item_click_attr && item_click_attr.as_string()[0] != '\0')
306+
step.action.item_click_ref = item_click_attr.as_string();
307+
const pugi::xml_attribute set_ref_attr = step_node.attribute(
308+
"set_ref");
309+
if (set_ref_attr && set_ref_attr.as_string()[0] != '\0')
310+
step.action.set_ref = set_ref_attr.as_string();
311+
301312
if (parse_float_pair_attr(step_node.attribute("mouse_pos"),
302313
step.action.mouse_x,
303314
step.action.mouse_y)) {
@@ -978,6 +989,16 @@ namespace {
978989
ctx->Yield(1);
979990
}
980991

992+
if (!action.set_ref.empty()) {
993+
ctx->SetRef(action.set_ref.c_str());
994+
ctx->Yield(1);
995+
}
996+
997+
if (!action.item_click_ref.empty()) {
998+
ctx->ItemClick(action.item_click_ref.c_str());
999+
ctx->Yield(1);
1000+
}
1001+
9811002
ImVec2 mouse_pos(0.0f, 0.0f);
9821003
if (resolve_action_mouse_pos(action, mouse_pos)) {
9831004
ctx->MouseMoveToPos(mouse_pos);
@@ -1721,4 +1742,36 @@ register_layout_dump_synthetic_rect(const char* kind, const char* label,
17211742
#endif
17221743
}
17231744

1745+
void
1746+
register_test_engine_item_label(const char* label, bool openable)
1747+
{
1748+
#if defined(IMGUI_ENABLE_TEST_ENGINE)
1749+
if (label == nullptr || label[0] == '\0')
1750+
return;
1751+
ImGuiContext* ui_ctx = ImGui::GetCurrentContext();
1752+
if (ui_ctx == nullptr)
1753+
return;
1754+
const ImVec2 min = ImGui::GetItemRectMin();
1755+
const ImVec2 max = ImGui::GetItemRectMax();
1756+
if (max.x <= min.x || max.y <= min.y)
1757+
return;
1758+
const int ordinal = ++g_layout_dump_synthetic_item_counter;
1759+
char id_source[128] = {};
1760+
std::snprintf(id_source, sizeof(id_source), "##imiv_test_item_%d",
1761+
ordinal);
1762+
const ImGuiID id = ImGui::GetID(id_source);
1763+
if (id == 0)
1764+
return;
1765+
const ImRect bb(min, max);
1766+
ImGuiItemStatusFlags flags = ImGuiItemStatusFlags_None;
1767+
if (openable)
1768+
flags |= ImGuiItemStatusFlags_Openable;
1769+
ImGuiTestEngineHook_ItemAdd(ui_ctx, id, bb, nullptr);
1770+
ImGuiTestEngineHook_ItemInfo(ui_ctx, id, label, flags);
1771+
#else
1772+
(void)label;
1773+
(void)openable;
1774+
#endif
1775+
}
1776+
17241777
} // namespace Imiv

src/imiv/imiv_test_engine.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,7 @@ register_layout_dump_synthetic_item(const char* kind, const char* label);
8484
void
8585
register_layout_dump_synthetic_rect(const char* kind, const char* label,
8686
const ImVec2& min, const ImVec2& max);
87+
void
88+
register_test_engine_item_label(const char* label, bool openable = false);
8789

8890
} // namespace Imiv

0 commit comments

Comments
 (0)