From a6335429e5166ad4f7cd61de30a9cf38853723cc Mon Sep 17 00:00:00 2001 From: Wang Zichong Date: Wed, 8 Jul 2026 13:26:08 +0800 Subject: [PATCH] rename binary name to gammaray-mcp-bridge --- .gitignore | 3 ++ PLAN.md | 85 ++++++++++++++++++++------------------- README.md | 32 ++++++++++----- bridge/CMakeLists.txt | 12 +++--- bridge/run.sh | 4 +- bridge/src/main.cpp | 6 +-- tests/README.md | 11 +++-- tests/conftest.py | 2 +- tests/run_tests.sh | 2 +- tests/run_widget_tests.sh | 25 +++++++++++- 10 files changed, 114 insertions(+), 68 deletions(-) diff --git a/.gitignore b/.gitignore index 184bbc3..01edc6d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ build/ bridge/build/ install-prefix/ +# Compiled test binaries (not committed) +tests/testapp/widget_test_app + # Python __pycache__/ .pytest_cache/ diff --git a/PLAN.md b/PLAN.md index 60460ca..a7104e9 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,19 +1,19 @@ -# QML SceneGraph MCP Bridge — Plan +# GammaRay MCP Bridge — Plan ## Goal -Build an MCP server that exposes QML SceneGraph introspection data (from GammaRay's probe) to LLMs for assisted debugging. The bridge is a **GammaRay client**: it connects to a probe injected into the target Qt/QML app, reads the same models the GammaRay GUI uses, and translates them into MCP JSON-RPC tool calls. +Build an MCP server that exposes GammaRay probe introspection data (QML SceneGraph, QML items, and Qt Widgets) to LLMs for assisted debugging. The bridge is a **GammaRay client**: it connects to a probe injected into the target Qt app, reads the same models the GammaRay GUI uses, and translates them into MCP JSON-RPC tool calls. ## Architecture ``` Target Qt/QML App - │ GammaRay probe injected (preload/attach), loads quickinspector plugin + │ GammaRay probe injected (preload/attach), loads quickinspector + widgetinspector plugins │ │ GammaRay binary protocol over TCP/local socket │ (common/protocol.h — QDataStream-based, NOT JSON-RPC) ▼ -QML SceneGraph MCP Bridge (new project, GPL-2.0-or-later) +GammaRay MCP Bridge (new project, GPL-2.0-or-later) │ Links gammaray_client + gammaray_common (installed from source) │ Uses ClientConnectionManager to connect, ObjectBroker to get models │ Connection is lazy: bridge starts without a probe, MCP client calls @@ -99,7 +99,7 @@ Caveats of the FetchContent approach: ```cmake cmake_minimum_required(VERSION 3.16) -project(QmlSceneGraphMcpBridge LANGUAGES CXX) +project(GammaRayMcpBridge LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) # qtmcp requires C++20 set(CMAKE_AUTOMOC ON) @@ -119,7 +119,7 @@ set(QT_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) set(QT_BUILD_TESTS OFF CACHE BOOL "" FORCE) FetchContent_MakeAvailable(qtmcp) -add_executable(qml-sg-mcp-bridge +add_executable(gammaray-mcp-bridge src/main.cpp src/gammaray_session.cpp # wraps ClientConnectionManager + ObjectBroker src/gammaray_session.h @@ -127,7 +127,7 @@ add_executable(qml-sg-mcp-bridge src/scenegraph_tools.h ) -target_link_libraries(qml-sg-mcp-bridge PRIVATE +target_link_libraries(gammaray-mcp-bridge PRIVATE gammaray_client # VERIFIED: no GammaRay:: namespace gammaray_common Qt6::Core @@ -139,7 +139,7 @@ target_link_libraries(qml-sg-mcp-bridge PRIVATE ) # qtmcp shared libs + stdio plugin live in build tree; make runtime find them: -set_target_properties(qml-sg-mcp-bridge PROPERTIES +set_target_properties(gammaray-mcp-bridge PROPERTIES BUILD_RPATH "$;$" ) ``` @@ -152,7 +152,7 @@ cmake --build build # Run with probe libs + qtmcp runtime libs on the path: LD_LIBRARY_PATH=$(pwd)/install-prefix/lib QT_QPA_PLATFORM=offscreen \ QT_PLUGIN_PATH=$PWD/build/lib/x86_64-linux-gnu/qt6/plugins \ - ./build/qml-sg-mcp-bridge --connect tcp://127.0.0.1:11732 + ./build/gammaray-mcp-bridge --connect tcp://127.0.0.1:11732 ``` ### ❗ MUST use QApplication, not QCoreApplication @@ -173,7 +173,7 @@ int main(int argc, char **argv) { // ... parse --connect ... QMcpServer server(QStringLiteral("stdio")); // backend name in ctor, no default - server.setInstructions("QML SceneGraph introspection via GammaRay"); + server.setInstructions("QML SceneGraph & Qt Widget introspection via GammaRay"); auto *tools = new SceneGraphTools(&server); // holds GammaRay client connection server.registerToolSet(tools, { @@ -505,7 +505,7 @@ When in doubt, these files have the ground truth: - ✅ **Step 1 done**: GammaRay 3.4.0 built + installed to `install-prefix/`. Protocol version 38. - ✅ **Minimal client built & run**: `minimal-client/` links `gammaray_client` + `gammaray_common`, connects to a live probe, receives `ready()`, fetches `QuickSceneGraphModel` / `QuickWindowModel` via `ObjectBroker` — clean exit. Proves the client ABI + protocol path is viable for the bridge. - ✅ **qtmcp FetchContent viable**: `Qt6::McpServer`/`Qt6::McpCommon` build & link from a `FetchContent_MakeAvailable()` call. No separate clone/install. -- ✅ **Bridge scaffold built & working end-to-end** (`bridge/`): `qml-sg-mcp-bridge` links GammaRay client + qtmcp, runs as a stdio MCP server. Smoke test (`/tmp/opencode/smoke_test.py`) against a live probe confirms: +- ✅ **Bridge scaffold built & working end-to-end** (`bridge/`): `gammaray-mcp-bridge` links GammaRay client + qtmcp, runs as a stdio MCP server. Smoke test against a live probe confirms: - `initialize` → serverInfo + capabilities + protocolVersion `2024-11-05` ✅ - `tools/list` → 15 tools registered: `connectProbe`, `connectProbeDefault`, `disconnectProbe`, `getMaterialProperties`, `getMaterialShaders`, `getNodeAdjacency`, `getNodeVertices`, `getShaderSource`, `listQuickWindows`, `listScenegraphNodes`, `probeStatus`, `selectQuickWindow`, `selectScenegraphNode`, `setRenderMode`, `setSlowMode` ✅ - `probeStatus` (before connect) → `{"ready":false,"state":"disconnected","url":""}` ✅ @@ -550,39 +550,38 @@ cmake --build build ``` `run.sh` sets `LD_LIBRARY_PATH` (GammaRay + qtmcp libs) and `QT_PLUGIN_PATH` (qtmcp `mcpserverbackend/libqmcpserverstdio.so`) and `QT_QPA_PLATFORM=offscreen`. -## Qt Widget support (planned) +## Qt Widget support (implemented) -GammaRay 的 widget inspector 与 quick inspector 共享同一套底层架构(`PropertyController`、`SelectionModelClient`、`RemoteModel`),大部分 QML 工具的模式可直接复用。 +GammaRay 的 widget inspector 与 quick inspector 共享同一套底层架构(`PropertyController`、`SelectionModelClient`、`RemoteModel`),大部分 QML 工具的模式已直接复用。 -### 可提供的 widget 工具 +### 已实现的 widget 工具 **导航(与 QML 类似,模型名不同)** -- `listWidgets()` — 遍历 `com.kdab.GammaRay.WidgetTree` 模型,返回 widget 层次树 -- `selectWidget(address)` — 通过 `SelectionModelClient` 选取 widget,触发 `PropertyController` 填充子模型(属性、attribute 等) -- `listTopLevelWindows()`(已有 `listQuickWindows`,可在 widget 应用中继续使用) +- ✅ `listWidgets()` — 遍历 `com.kdab.GammaRay.WidgetTree` 模型,返回 widget 层次树 +- ✅ `selectWidget(address)` — 通过 `SelectionModelClient` 选取 widget,触发 `PropertyController` 填充子模型(属性、attribute 等) **属性/attribute(与 QML 共享读取实现)** -- `getWidgetProperties(address)` — 读取同一套 `AggregatedPropertyModel`(通过 `PropertyController`),模型对象名 `com.kdab.GammaRay.Widget.
.properties`,与 `getItemProperties` 共享实现代码 -- `getWidgetAttributes(address)` — 读取 `widgetAttributeModel`(`AttributeModel`),列出 Qt::WidgetAttribute 值 +- ✅ `getWidgetProperties(address)` — 读取同一套 `AggregatedPropertyModel`(通过 `PropertyController`),与 `getItemProperties` 共享实现代码 +- ✅ `getWidgetAttributes(address)` — 读取 `widgetAttributeModel`(`AttributeModel`),列出 Qt::WidgetAttribute 值 **视觉/分析(新功能,需 RemoteViewInterface)** -- `grabWidget(address)` — 通过 `WidgetRemoteView`(`com.kdab.GammaRay.WidgetRemoteView`)抓取 widget 截图,异步图像传输 -- `analyzePainting(address)` — 触发 `PaintAnalyzer` 捕获绘制操作 -- `exportWidgetAsSvg(address)` — 通过 `WidgetInspectorInterface` 导出 SVG -- `exportWidgetAsUiFile(address)` — 导出 Qt Designer .ui 文件 +- ❌ `grabWidget(address)` — 通过 `WidgetRemoteView`(`com.kdab.GammaRay.WidgetRemoteView`)抓取 widget 截图,异步图像传输 +- ❌ `analyzePainting(address)` — 触发 `PaintAnalyzer` 捕获绘制操作 +- ❌ `exportWidgetAsSvg(address)` — 通过 `WidgetInspectorInterface` 导出 SVG +- ❌ `exportWidgetAsUiFile(address)` — 导出 Qt Designer .ui 文件 **控制** -- `setInputRedirection(enabled)` — 允许将鼠标/键盘事件转发到目标 widget +- ❌ `setInputRedirection(enabled)` — 允许将鼠标/键盘事件转发到目标 widget ### 实现策略 | 难度 | 工作项 | 说明 | |---|---|---| -| 低 | `listWidgets()` / `selectWidget()` | 直接复用 `listQuickItems()` / `selectQuickItem()` 的代码模式,模型名改为 `com.kdab.GammaRay.WidgetTree` | -| 低 | `getWidgetProperties()` | 与 `getItemProperties()` 完全一样,都读 `AggregatedPropertyModel`,可提取共享函数 | -| 中 | `getWidgetAttributes()` | 需要读取 `widgetAttributeModel`(`PropertyController` 子模型),模式与 `getMaterialProperties()` 类似 | -| 中 | `WidgetInspectorInterface` 代理 | IID `com.kdab.GammaRay.WidgetInspector`,用于 `export*`/`setInputRedirection` 等方法,类似 `QuickInspectorProxy` | -| 高 | `grabWidget()` / `analyzePainting()` | 需要 `RemoteViewInterface` 的 `TransferImage` 异步路径,模式与 `MaterialExtensionInterface` 的 `gotShader` 信号类似 | +| ✅ 已完成 | `listWidgets()` / `selectWidget()` | 直接复用 `listQuickItems()` / `selectQuickItem()` 的代码模式,模型名改为 `com.kdab.GammaRay.WidgetTree` | +| ✅ 已完成 | `getWidgetProperties()` | 与 `getItemProperties()` 完全一样,都读 `AggregatedPropertyModel`,已提取共享函数 `readAggregatedPropertyModel()` | +| ✅ 已完成 | `getWidgetAttributes()` | 读取 `widgetAttributeModel`(`PropertyController` 子模型),模式与 `getMaterialProperties()` 类似 | +| ✅ 已完成 | `WidgetInspectorInterface` 代理 | IID `com.kdab.GammaRay.WidgetInspector`,用于 `export*`/`setInputRedirection` 等方法,类似 `QuickInspectorProxy` | +| ❌ 未实现 | `grabWidget()` / `analyzePainting()` | 需要 `RemoteViewInterface` 的 `TransferImage` 异步路径,模式与 `MaterialExtensionInterface` 的 `gotShader` 信号类似 | ### 与 QML 工具的关键区别 @@ -594,18 +593,20 @@ GammaRay 的 widget inspector 与 quick inspector 共享同一套底层架构( ## Next steps -### QML SceneGraph 工具(当前阶段) +### 已完成的阶段 + +- ✅ **QML SceneGraph 工具**:listScenegraphNodes、getNodeVertices、getNodeAdjacency、getMaterialShaders、getShaderSource、getMaterialProperties、setRenderMode、setSlowMode +- ✅ **QML Item 属性工具**:listQuickItems、selectQuickItem、getItemProperties +- ✅ **Qt Widget 工具**:listWidgets、selectWidget、getWidgetProperties、getWidgetAttributes +- ✅ **跨平台保护**:QuickInspector 和 WidgetInspector 的代理注册均已添加条件守卫,避免在未加载对应插件的 target 进程中崩溃 +- ✅ **测试套件**:27 个 QML 测试 + 7 个 widget 测试,共 34 个集成测试 + +### 剩余工作 1. **Test `getShaderSource` async path end-to-end** — needs a QML app with real shaders (current test app uses software renderer where GeometryNodes have `nullptr` geometry). -2. **Test geometry/material tools against a QML app with real QSGGeometry data** — current test app `relayout.qml` uses software renderer so `hasGeometry: false` and empty sub-models are correct behavior. -3. **Implement `grabTexture(objectId)`** — `TextureExtension` async image grab via `RemoteViewInterface` `TransferImage`. Follows the `MaterialExtensionInterface` proxy pattern. -4. **Fix `listScenegraphNodes` first-call fill stability** — single-call instead of caller needing a second call. -5. **Build `--connect` startup flag integration** with systemd service for auto-attach during development. - -### Qt Widget 支持(未来阶段) - -1. 创建 widget_tools.{h,cpp},注册 `listWidgets` / `selectWidget` / `getWidgetProperties` / `getWidgetAttributes` MCP 工具 -2. 从 `scenegraph_tools.cpp` 提取共享的 `AggregatedPropertyModel` 读取函数,供 QML 和 widget 工具共用 -3. 创建 `WidgetInspectorInterface` 代理(类似 `QuickInspectorProxy`),处理 `export*`/`setInputRedirection` -4. 实现 `RemoteViewInterface` 异步图像传输路径(grabWidget/analyzePainting) -5. 添加 widget 测试应用(简单 QWidget 窗口 + 按钮/布局),编写 widget 工具的集成测试 +2. **Test geometry/material tools against a QML app with real QSGGeometry data** — current test app uses software renderer so `hasGeometry: false` and empty sub-models are correct behavior. +3. **Implement `grabWidget(objectId)`** — `RemoteViewInterface` async image grab via `TransferImage`. Follows the `MaterialExtensionInterface` proxy pattern. +4. **Implement `grabTexture(objectId)`** — `TextureExtension` async image grab via `RemoteViewInterface` `TransferImage`. +5. **Implement `analyzePainting()` / `exportWidgetAsSvg()` / `exportWidgetAsUiFile()`** — via `WidgetInspectorInterface` proxy. +6. **Fix `listScenegraphNodes` first-call fill stability** — single-call instead of caller needing a second call. +7. **Build `--connect` startup flag integration** with systemd service for auto-attach during development. diff --git a/README.md b/README.md index f13fab6..20f927e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# QML SceneGraph MCP Bridge +# GammaRay MCP Bridge -A [MCP](https://modelcontextprotocol.io/) server that bridges [GammaRay](https://github.com/KDAB/GammaRay) probe introspection data into MCP tools, enabling LLMs to inspect and debug Qt Quick / QML scene graphs, items, geometry, and materials. +A [MCP](https://modelcontextprotocol.io/) server that bridges [GammaRay](https://github.com/KDAB/GammaRay) probe introspection data into MCP tools, enabling LLMs to inspect and debug Qt Quick / QML scene graphs, items, geometry, materials, and Qt Widgets. ## Architecture @@ -10,7 +10,7 @@ Target Qt/QML App ──TCP──► GammaRay MCP Bridge ──stdio──► injected) qtmcp MCP server) Claude Desktop, etc.) ``` -The bridge is a GammaRay **client**: it connects to a probe injected into a target Qt/QML app, reads the same models the GammaRay GUI uses, and translates them into MCP JSON-RPC tool calls over stdio. +The bridge is a GammaRay **client**: it connects to a probe injected into a target Qt app, reads the same models the GammaRay GUI uses, and translates them into MCP JSON-RPC tool calls over stdio. Supports both QML Quick apps (SceneGraph, QML items) and Qt Widget apps (widget hierarchy, properties, attributes). ## Prerequisites @@ -46,6 +46,10 @@ cmake --build build install-prefix/bin/gammaray --inject-only --listen tcp://127.0.0.1:11732 \ --injector preload /usr/lib/qt6/bin/qml /path/to/app.qml -platform offscreen +# Or inject into a widget app +install-prefix/bin/gammaray --inject-only --listen tcp://127.0.0.1:11732 \ + --injector preload /path/to/widget-app + # Start the bridge (stdio MCP server) bridge/run.sh ``` @@ -62,7 +66,7 @@ The bridge starts in lazy-connect mode. Call `connectProbe("127.0.0.1", 11732)` | `disconnectProbe()` | Drop connection and forget URL | | `probeStatus()` | Report connection state | -### Navigation +### QML Navigation | Tool | Description | |---|---| | `listQuickWindows()` | List QQuickWindows in the target app | @@ -92,15 +96,25 @@ The bridge starts in lazy-connect mode. Call `connectProbe("127.0.0.1", 11732)` | `setRenderMode(mode)` | Set render mode (NormalRendering, VisualizeOverdraw, etc.) | | `setSlowMode(enabled)` | Toggle continuous rendering | +### Widget inspection +| Tool | Description | +|---|---| +| `listWidgets()` | Recursive QWidget hierarchy (types, names, visibility) | +| `selectWidget(address)` | Select a widget, populating its property and attribute models | +| `getWidgetProperties(address)` | Get all Q_PROPERTY values (geometry, font, palette, window flags, etc.) | +| `getWidgetAttributes(address)` | Get Qt::WidgetAttribute flags (acceptDrops, enabled, etc.) | + ## Testing ```bash -# Unit tests only (no probe needed): -./tests/run_tests.sh --no-probe +# QML test suite (requires a QML app probe): +./tests/run_tests.sh -# Full integration test suite (probe required): -./tests/run_tests.sh --no-probe # skips probe-dependent tests -./tests/run_tests.sh -v # verbose +# Widget test suite (requires a widget app probe): +./tests/run_widget_tests.sh + +# Unit tests only: +./tests/run_tests.sh --no-probe ``` See `tests/README.md` for details. diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt index 08cc2e3..0932656 100644 --- a/bridge/CMakeLists.txt +++ b/bridge/CMakeLists.txt @@ -1,11 +1,11 @@ -# QML SceneGraph MCP Bridge +# GammaRay MCP Bridge # SPDX-License-Identifier: GPL-2.0-or-later # # Links GammaRay client libs (GPL-2.0-or-later) => this bridge must be # GPL-compatible. qtmcp is consumed under its GPL-2.0-only option (compatible). cmake_minimum_required(VERSION 3.16) -project(QmlSceneGraphMcpBridge LANGUAGES CXX) +project(GammaRayMcpBridge LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) # qtmcp requires C++20 set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -37,7 +37,7 @@ find_package(GammaRay REQUIRED) # --- Qt6 components for the bridge target (Qt6 already found globally by qtmcp) --- find_package(Qt6 COMPONENTS Core Gui Widgets Network REQUIRED) -add_executable(qml-sg-mcp-bridge +add_executable(gammaray-mcp-bridge src/main.cpp src/gammaray_session.cpp src/gammaray_session.h @@ -56,7 +56,7 @@ add_executable(qml-sg-mcp-bridge src/widget_inspector_proxy.h ) -target_link_libraries(qml-sg-mcp-bridge PRIVATE +target_link_libraries(gammaray-mcp-bridge PRIVATE gammaray_client # VERIFIED: no GammaRay:: namespace gammaray_common Qt6::Core @@ -68,7 +68,7 @@ target_link_libraries(qml-sg-mcp-bridge PRIVATE ) # Add GammaRay source common/ for message.h (not in installed headers) -target_include_directories(qml-sg-mcp-bridge PRIVATE +target_include_directories(gammaray-mcp-bridge PRIVATE /home/blumia/Sources/GammaRay/common ) @@ -77,7 +77,7 @@ target_include_directories(qml-sg-mcp-bridge PRIVATE # run time (see run.sh) so Qt loads mcpserverbackend/libqmcpserverstdio.so. # Also add GammaRay's install lib dir for libgammaray_*.so. set(_GR_LIBDIR "$") -set_target_properties(qml-sg-mcp-bridge PROPERTIES +set_target_properties(gammaray-mcp-bridge PROPERTIES BUILD_RPATH "$;${CMAKE_SOURCE_DIR}/../install-prefix/lib" INSTALL_RPATH "$ORIGIN/../lib" ) diff --git a/bridge/run.sh b/bridge/run.sh index 7cab1b8..18f47d6 100755 --- a/bridge/run.sh +++ b/bridge/run.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Run the QML SceneGraph MCP bridge. +# Run the GammaRay MCP bridge. # Sets up LD_LIBRARY_PATH (GammaRay + qtmcp libs) and QT_PLUGIN_PATH (qtmcp stdio # backend plugin) so the bridge can find everything at runtime. # SPDX-License-Identifier: GPL-2.0-or-later @@ -10,4 +10,4 @@ BLD="$HERE/build" export LD_LIBRARY_PATH="$ROOT/install-prefix/lib:$BLD/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" export QT_PLUGIN_PATH="$BLD/lib/x86_64-linux-gnu/qt6/plugins:${QT_PLUGIN_PATH:-}" export QT_QPA_PLATFORM=offscreen -exec "$BLD/qml-sg-mcp-bridge" "$@" +exec "$BLD/gammaray-mcp-bridge" "$@" diff --git a/bridge/src/main.cpp b/bridge/src/main.cpp index 7a41c7e..fb07c60 100644 --- a/bridge/src/main.cpp +++ b/bridge/src/main.cpp @@ -1,5 +1,5 @@ /* - qml-sg-mcp-bridge — MCP server bridging QML SceneGraph introspection. + gammaray-mcp-bridge — MCP server bridging GammaRay introspection. SPDX-FileCopyrightText: 2026 The GammaRay MCP Bridge Authors SPDX-License-Identifier: GPL-2.0-or-later @@ -33,12 +33,12 @@ int main(int argc, char **argv) qSetMessagePattern(QStringLiteral("[%{type}] %{message}")); QApplication app(argc, argv); - app.setApplicationName(QStringLiteral("qml-sg-mcp-bridge")); + app.setApplicationName(QStringLiteral("gammaray-mcp-bridge")); app.setApplicationVersion(QStringLiteral("0.1.0")); app.setOrganizationName(QStringLiteral("KDAB")); QCommandLineParser parser; - parser.setApplicationDescription(QStringLiteral("QML SceneGraph MCP Bridge")); + parser.setApplicationDescription(QStringLiteral("GammaRay MCP Bridge")); parser.addHelpOption(); parser.addVersionOption(); diff --git a/tests/README.md b/tests/README.md index 17d38bc..a646f2c 100644 --- a/tests/README.md +++ b/tests/README.md @@ -20,13 +20,18 @@ end-to-end against a real GammaRay probe. | `test_navigation.py` | `listQuickWindows`, `selectQuickWindow`, `listQuickItems`, `listScenegraphNodes` | Yes | | `test_items.py` | `selectQuickItem`, `getItemProperties`, position, visual properties, groups | Yes | | `test_scenegraph.py` | `selectScenegraphNode`, `getNodeVertices`, `getNodeAdjacency`, `getMaterialShaders`, `getMaterialProperties`, `setRenderMode`, `setSlowMode` | Yes | +| `test_widgets.py` | `listWidgets`, `selectWidget`, `getWidgetProperties`, `getWidgetAttributes` | Yes (--widget-app) | -## Test QML application +## Test applications `testapp/main.qml` is a simple Qt Quick Controls application that exercises all -the tools: buttons, switches, sliders, a list view with delegate items, and +the QML tools: buttons, switches, sliders, a list view with delegate items, and visibility/opacity controls. +`testapp/widget_test_app.cpp` is a simple QWidget application (QLabel, QLineEdit, +QCheckBox, QGroupBox, QPushButton) for testing widget inspection tools. Use +`run_widget_tests.sh` to run tests against this app. + ## Configuration **`run_tests.sh` is the single configuration point.** All paths default to @@ -40,7 +45,7 @@ system package), override these by editing the variables at the top of | `GAMMARAY_LIB_DIR` | `install-prefix/lib` | Runtime library directory for GammaRay | | `QML_RUNNER` | `/usr/lib/qt6/bin/qml` | QML runtime executable | | `TEST_QML` | `testapp/main.qml` | Test QML application | -| `BRIDGE_EXE` | `bridge/build/qml-sg-mcp-bridge` | Bridge executable | +| `BRIDGE_EXE` | `bridge/build/gammaray-mcp-bridge` | Bridge executable | | `BRIDGE_RUN_SCRIPT` | `bridge/run.sh` | Bridge wrapper script | You can also override any of these via environment variables when calling diff --git a/tests/conftest.py b/tests/conftest.py index f64bfd4..ec808a3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -52,7 +52,7 @@ WIDGET_TEST_APP_SRC = Path(os.environ.get( )) BRIDGE_EXE = Path(os.environ.get( "BRIDGE_EXE", - str(PROJECT_ROOT / "bridge" / "build" / "qml-sg-mcp-bridge"), + str(PROJECT_ROOT / "bridge" / "build" / "gammaray-mcp-bridge"), )) BRIDGE_RUN_SCRIPT = Path(os.environ.get( "BRIDGE_RUN_SCRIPT", diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 1363166..039e8ce 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -24,7 +24,7 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" : "${GAMMARAY_LIB_DIR:=$PROJECT_ROOT/install-prefix/lib}" : "${QML_RUNNER:=/usr/lib/qt6/bin/qml}" : "${TEST_QML:=$SCRIPT_DIR/testapp/main.qml}" -: "${BRIDGE_EXE:=$PROJECT_ROOT/bridge/build/qml-sg-mcp-bridge}" +: "${BRIDGE_EXE:=$PROJECT_ROOT/bridge/build/gammaray-mcp-bridge}" : "${BRIDGE_RUN_SCRIPT:=$PROJECT_ROOT/bridge/run.sh}" # Export so conftest.py can read them diff --git a/tests/run_widget_tests.sh b/tests/run_widget_tests.sh index 5d1d3b5..f873f09 100755 --- a/tests/run_widget_tests.sh +++ b/tests/run_widget_tests.sh @@ -20,9 +20,32 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" : "${GAMMARAY_LIB_DIR:=$PROJECT_ROOT/install-prefix/lib}" : "${WIDGET_TEST_APP_BIN:=$SCRIPT_DIR/testapp/widget_test_app}" : "${WIDGET_TEST_APP_SRC:=$SCRIPT_DIR/testapp/widget_test_app.cpp}" -: "${BRIDGE_EXE:=$PROJECT_ROOT/bridge/build/qml-sg-mcp-bridge}" +: "${BRIDGE_EXE:=$PROJECT_ROOT/bridge/build/gammaray-mcp-bridge}" : "${BRIDGE_RUN_SCRIPT:=$PROJECT_ROOT/bridge/run.sh}" +# --------------------------------------------------------------------------- +# Ensure the widget test app is compiled +# --------------------------------------------------------------------------- +_compile_widget_app() { + echo "Compiling widget test app..." + local cflags + cflags=$(pkg-config --cflags Qt6Core Qt6Gui Qt6Widgets 2>/dev/null) || { + echo "Error: pkg-config for Qt6 not found. Install qt6-base-dev or set WIDGET_TEST_APP_BIN to a pre-built binary." + exit 1 + } + local libs + libs=$(pkg-config --libs Qt6Core Qt6Gui Qt6Widgets 2>/dev/null) || { + echo "Error: pkg-config for Qt6 libs not found." + exit 1 + } + g++ -std=c++17 -fPIC "$WIDGET_TEST_APP_SRC" -o "$WIDGET_TEST_APP_BIN" \ + $cflags $libs + echo "Widget test app compiled: $WIDGET_TEST_APP_BIN" +} + +if [ ! -f "$WIDGET_TEST_APP_BIN" ]; then + _compile_widget_app +fi export GAMMARAY_BIN export GAMMARAY_LIB_DIR export WIDGET_TEST_APP_BIN