diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 600774dc96f..8af55657a3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,17 @@ jobs: run: | set -x scripts/rip-environment runtests -p + - name: Upload UI smoke screenshots + if: always() + uses: actions/upload-artifact@v7 + with: + name: ui-smoke-screenshots-gcc + path: | + tests/ui-smoke/**/screenshot.png + tests/ui-smoke/**/confirm.png + tests/ui-smoke/**/diff.png + if-no-files-found: ignore + retention-days: 14 - name: Verify no untracked or modified files after test run: | .github/scripts/verify-clean-repo.sh @@ -112,6 +123,17 @@ jobs: run: | set -x scripts/rip-environment runtests -p + - name: Upload UI smoke screenshots + if: always() + uses: actions/upload-artifact@v7 + with: + name: ui-smoke-screenshots-clang + path: | + tests/ui-smoke/**/screenshot.png + tests/ui-smoke/**/confirm.png + tests/ui-smoke/**/diff.png + if-no-files-found: ignore + retention-days: 14 - name: Verify no untracked or modified files after test run: | .github/scripts/verify-clean-repo.sh diff --git a/debian/control.top.in b/debian/control.top.in index 596063ea377..0ff33203770 100644 --- a/debian/control.top.in +++ b/debian/control.top.in @@ -47,6 +47,7 @@ Build-Depends: tk@TCLTK_VERSION@-dev, xvfb , x11-xserver-utils , + imagemagick , python3-opengl , python3-pyqt5 , python3-pyqt5.qsci , diff --git a/tests/ui-smoke/.gitignore b/tests/ui-smoke/.gitignore index 05fea704e9d..a740b9a71da 100644 --- a/tests/ui-smoke/.gitignore +++ b/tests/ui-smoke/.gitignore @@ -5,3 +5,9 @@ linuxcnc.err linuxcnc.pid ui-smoke.out ui-smoke.err +# confirm.png is the per-run shot; diff.png is its comparison to the +# committed reference.png (which IS tracked, so it is not listed here). +screenshot.png +confirm.png +ui-smoke-qt.png +diff.png diff --git a/tests/ui-smoke/README b/tests/ui-smoke/README index eb9ea8ef0ff..3a816fdcdb0 100644 --- a/tests/ui-smoke/README +++ b/tests/ui-smoke/README @@ -19,6 +19,35 @@ Shared helpers live in _lib/: checkresult.sh shared pass/fail predicate skip-if-missing.sh shared skip predicate +Failure diagnostics (failure path only, no cost on a green run): + crashdump.sh arms a core dump and prints a native backtrace if a GUI + segfaults (the Qt/dbus/GL frames PYTHONFAULTHANDLER misses) + screenshot.sh photographs the Xvfb root window before teardown. On a + failure it captures the cause (a GUI hung on a blocking + modal leaves no core and no traceback); on a clean Phase 2 + run it captures confirm.png, the GUI in its post-movement + idle state (final DRO / toolpath) for visual confirmation. + CI uploads both as build artifacts (ui-smoke-screenshots-* + in ci.yml). + compare.sh on a clean run, compares confirm.png to the committed + known-good reference.png (ImageMagick) and writes a + highlighted diff.png, also uploaded as an artifact. + +Reference images: + reference.png committed per-GUI known-good shot (in a --run-program test + directory); diff.png is confirm.png compared against it. + The comparison NEVER fails a test: freetype/font versions + differ across distros, so some drift is expected. The diff + is only recorded, not used to gate the test. + + Create or refresh the references on a built run-in-place tree: + . scripts/rip-environment + tests/ui-smoke/_lib/make-references.sh # all run-program GUIs + tests/ui-smoke/_lib/make-references.sh axis # just one + This sets UI_SMOKE_UPDATE_REFERENCE=1, which makes compare.sh save each + clean confirm shot as that test's reference.png. Review the PNGs before + committing; they are baselines from the generating machine's fonts. + Skip vs fail policy: the only condition we skip on is xvfb-run absence (rare local dev env). Python and gi typelib deps the GUIs need are declared in debian/control under !nocheck so apt-get build-dep diff --git a/tests/ui-smoke/_lib/compare.sh b/tests/ui-smoke/_lib/compare.sh new file mode 100644 index 00000000000..a1f368b65cb --- /dev/null +++ b/tests/ui-smoke/_lib/compare.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# Known-good image comparison for the UI smoke confirm shots. Complements +# screenshot.sh: that grabs confirm.png on a clean run; this compares it to +# a committed reference.png and writes a visual diff.png. Like screenshot.sh +# it carries no state and is a logged no-op whenever it cannot run, so it can +# never turn a pass into a fail. +# +# Policy: we never fail a test on the image difference. freetype/font +# versions differ across distros, so some drift is expected; the diff is +# here to record what changed, not to gate. The function always returns 0. +# +# Local "make a known-good image" workflow: run a test with +# UI_SMOKE_UPDATE_REFERENCE=1 (see make-references.sh) and the freshly +# captured shot is saved as the committed reference instead of compared. + +# Pick the ImageMagick compare entry point: IM7 "magick compare", else IM6 +# "compare". Echoes nothing and returns 1 if neither is present. +_compare_cmd() { + if command -v magick >/dev/null 2>&1; then + echo "magick compare" + elif command -v compare >/dev/null 2>&1; then + echo "compare" + else + return 1 + fi +} + +# compare_to_reference +# Compare the captured shot to the committed reference, writing a highlighted +# diff image. Always returns 0. +compare_to_reference() { + shot="$1" + reference="$2" + diff="$3" + + if [ ! -s "$shot" ]; then + echo "compare: no shot at $shot, skipping" + return 0 + fi + + # Update mode: adopt this shot as the new known-good reference. + if [ "${UI_SMOKE_UPDATE_REFERENCE:-}" = "1" ]; then + if cp -f "$shot" "$reference"; then + echo "compare: saved reference $reference (UI_SMOKE_UPDATE_REFERENCE=1)" + else + echo "compare: failed to save reference $reference" + fi + return 0 + fi + + if [ ! -s "$reference" ]; then + echo "compare: no reference at $reference yet, skipping (run with UI_SMOKE_UPDATE_REFERENCE=1 to create one)" + return 0 + fi + + cmd=$(_compare_cmd) || { + echo "compare: no ImageMagick compare available, skipping" + return 0 + } + + # -metric AE: count of differing pixels (interpretable); -fuzz absorbs + # anti-aliasing jitter. compare exits 0 (identical), 1 (differ) or 2 + # (error, e.g. the shots are different sizes). We log the outcome and + # always succeed. + metric=$($cmd -metric AE -fuzz 5% "$reference" "$shot" "$diff" 2>&1) + rc=$? + case "$rc" in + 0) echo "compare: $shot matches $reference (AE=$metric)" ;; + 1) echo "compare: $shot differs from $reference (AE=$metric differing pixels); diff at $diff" ;; + *) echo "compare: could not compare $shot to $reference (rc=$rc): $metric" ;; + esac + return 0 +} diff --git a/tests/ui-smoke/_lib/drive.py b/tests/ui-smoke/_lib/drive.py index ad7e89a01fe..35deb1d3d7c 100755 --- a/tests/ui-smoke/_lib/drive.py +++ b/tests/ui-smoke/_lib/drive.py @@ -32,6 +32,13 @@ # stability check catches that. STATE_STABILITY_S = 0.5 STATE_RETRY_BUDGET = 6 +# Pause after homing before requesting AUTO. gmoccapy only enables AUTO +# once it has processed the all-homed signal in its own event loop (and +# re-asserts MANUAL itself on that signal). Requesting AUTO before then is +# rejected: it bounces back to MANUAL with an "It is not possible to +# change to Auto Mode" warning. ensure_mode would retry and win, but the +# warning lingers on screen; this settle lets the GUI catch up first. +POST_HOME_SETTLE_S = 2.0 # linuxcnc launcher PID, written to linuxcnc.pid by the launcher and read # once at startup. The driver watches it so a GUI crash, which tears @@ -297,6 +304,11 @@ def run_program(cmd, stat, ngc_path, expect_delta_mm, tol, run_timeout): if not home_all(cmd, stat, timeout=60.0): return False + # Let the GUI react to the all-homed transition before requesting AUTO, + # so it does not reject the mode change (see POST_HOME_SETTLE_S). + time.sleep(POST_HOME_SETTLE_S) + stat.poll() + if not ensure_mode(cmd, stat, linuxcnc.MODE_AUTO, "MODE_AUTO"): return False diff --git a/tests/ui-smoke/_lib/gmoccapy-prepare.sh b/tests/ui-smoke/_lib/gmoccapy-prepare.sh new file mode 100644 index 00000000000..08bfaffc5ee --- /dev/null +++ b/tests/ui-smoke/_lib/gmoccapy-prepare.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Sourced by the gmoccapy ui-smoke tests (smoke and quit) to run gmoccapy +# against a writable copy of its sim config. Sets GMOCCAPY_INI to the +# mirrored ini path; the caller then execs run-gui.sh or quit-launch.sh +# with "$GMOCCAPY_INI". Must be sourced with LIB_DIR already set. +# +# gmoccapy writes its preferences file next to the config: with no +# PREFERENCE_FILE_PATH in the ini, getiniinfo falls back to +# /.pref. CI mounts the workspace read-only for the +# runtime user, so that write raises PermissionError partway through +# __init__ (during _get_pref_data, before the MDIHistory widget's +# _hal_init runs). gmoccapy pops an error dialog and limps on in a +# half-initialised state: the interp-idle handler then hits a widget with +# no .stat and throws a second dialog. Both vanish once the config dir is +# writable. Mirror it to tmp, same fix qtdragon-prepare.sh uses. + +: "${LIB_DIR:?gmoccapy-prepare.sh must be sourced with LIB_DIR set}" + +SRC_DIR="$(cd "$LIB_DIR/../../../configs/sim/gmoccapy" && pwd)" + +WORK_DIR="$(mktemp -d -t ui-smoke-gmoccapy.XXXXXX)" +trap 'rm -rf "$WORK_DIR"' EXIT +cp -r "$SRC_DIR/." "$WORK_DIR/" + +# Seed the preference file (config dir + .pref; MACHINE=gmoccapy) +# so the first-run "Important change(s)" modal stays hidden. That dialog +# runs a nested gtk loop, so under xvfb it never gets dismissed: it sits +# on top of the UI in the confirm shot and, worse, swallows the SIGTERM +# in the quit test (the loop keeps running after main_quit). A real user +# ticks "Don't show this again" once; hide_startup_messsage replicates +# that. The triple-s key matches gmoccapy's own (sic). +cat >"$WORK_DIR/gmoccapy.pref" <<'PREF' +[DEFAULT] +hide_startup_messsage = 99 +PREF + +# Consumed by the sourcing test.sh, which execs the launcher with it. +# shellcheck disable=SC2034 +GMOCCAPY_INI="$WORK_DIR/gmoccapy.ini" diff --git a/tests/ui-smoke/_lib/launch-env.sh b/tests/ui-smoke/_lib/launch-env.sh index 110319195be..0de0dca2ac3 100644 --- a/tests/ui-smoke/_lib/launch-env.sh +++ b/tests/ui-smoke/_lib/launch-env.sh @@ -29,3 +29,10 @@ export SDL_AUDIODRIVER=dummy # names the line; for a C/C++ crash (Qt, dbus, GL) it shows the Python # frame that called in. The native side is captured by crashdump.sh. export PYTHONFAULTHANDLER=1 + +# Xvfb virtual screen for the launchers' xvfb-run. There is no window +# manager under xvfb-run, so a GUI's maximize() is a no-op and it renders +# at its natural size; a screen smaller than the window clips the grab +# (the failure/confirm screenshot then misses panels). 1920x1080 fits +# every sim GUI so the whole window is captured. +export UI_SMOKE_XVFB_SCREEN="${UI_SMOKE_XVFB_SCREEN:-1920x1080x24}" diff --git a/tests/ui-smoke/_lib/launch.sh b/tests/ui-smoke/_lib/launch.sh index b66b4af1d00..ba2827c2aa6 100755 --- a/tests/ui-smoke/_lib/launch.sh +++ b/tests/ui-smoke/_lib/launch.sh @@ -43,6 +43,12 @@ DRIVER_TIMEOUT=180 . "$LIB_DIR/crashdump.sh" crashdump_arm +# Absolute path the offscreen-Qt self-grab writes to. An offscreen GUI +# runs with its cwd at the (writable) config mirror, not the test dir, so +# a relative name would land out of reach; pin it to the test dir, which +# is this shell's cwd. Harmless for the GTK GUIs, which ignore it. +export UI_SMOKE_QT_SHOT="$PWD/ui-smoke-qt.png" + # Export the per-invocation values so the inner bash -c receives them # as proper env vars (avoids embedding paths into the inner script # via quoting, which breaks on apostrophes / spaces). @@ -52,7 +58,7 @@ export CONFIG_INI LIB_DIR DRIVER_TIMEOUT # LIB_DIR and DRIVER_TIMEOUT are expanded by the inner bash (which sees # them via the exported env), not by the outer shell. # shellcheck disable=SC2016 -xvfb-run -a --server-args="-screen 0 1024x768x24" \ +xvfb-run -a --server-args="-screen 0 $UI_SMOKE_XVFB_SCREEN" \ timeout "$LINUXCNC_TIMEOUT" \ bash -c ' # Run linuxcnc in its own process group so we can signal the @@ -70,6 +76,29 @@ xvfb-run -a --server-args="-screen 0 1024x768x24" \ timeout "$DRIVER_TIMEOUT" python3 "$LIB_DIR/drive.py" "$@" >ui-smoke.out 2>ui-smoke.err DRIVE_RC=$? + # Photograph the root window before teardown, while DISPLAY is the + # Xvfb server and the GUI is still up. On failure the picture shows + # the cause (a hung GUI on a blocking modal leaves no core for + # crashdump.sh and no Python traceback). On a clean Phase 2 run it + # is a confirmation shot of the GUI in its post-movement idle state + # (final DRO / toolpath), so a reviewer can eyeball the result. The + # short settle lets the GUI repaint the final position first. + . "$LIB_DIR/screenshot.sh" + if [ "$DRIVE_RC" -ne 0 ]; then + screenshot_grab screenshot.png + else + case " $* " in + *" --run-program "*) + sleep 0.5 + screenshot_grab confirm.png + # Compare the confirm shot to the committed known-good + # reference and write a visual diff. Never fails the test. + . "$LIB_DIR/compare.sh" + compare_to_reference confirm.png reference.png diff.png + ;; + esac + fi + # Clean shutdown: GUI-specific quit first (lets linuxcnc end # its own SIGTERM trap run Cleanup which unloads halrun and # reaps shared memory). axis-remote works only for axis but is @@ -109,4 +138,10 @@ echo "=== ui-smoke.err ===" # If the GUI dumped a core, print its native backtrace. crashdump_report +# Note any screenshot so the CI artifact step and reviewer know it is +# there to download: screenshot.png on failure, confirm.png on a clean run. +[ -f screenshot.png ] && echo "=== screenshot: $TEST_DIR/screenshot.png ===" +[ -f confirm.png ] && echo "=== confirm: $TEST_DIR/confirm.png ===" +[ -f diff.png ] && echo "=== diff: $TEST_DIR/diff.png ===" + exit "$RC" diff --git a/tests/ui-smoke/_lib/make-references.sh b/tests/ui-smoke/_lib/make-references.sh new file mode 100755 index 00000000000..c8cd2136e25 --- /dev/null +++ b/tests/ui-smoke/_lib/make-references.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Generate (or refresh) the committed known-good reference.png images by +# running the ui-smoke tests with UI_SMOKE_UPDATE_REFERENCE=1, which makes +# compare.sh save each clean confirm shot as that test's reference.png +# instead of comparing against it. +# +# Run from a built run-in-place tree with the rip environment sourced: +# . scripts/rip-environment +# tests/ui-smoke/_lib/make-references.sh # all run-program GUIs +# tests/ui-smoke/_lib/make-references.sh axis # just one +# +# Only the GUIs that capture a confirm shot (the --run-program tests) have a +# reference. Review the resulting PNGs before committing; they are baselines +# from this machine's fonts and will drift on other distros (which is why the +# comparison never fails a test, only records a diff). + +set -u + +LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SMOKE_DIR="$(cd "$LIB_DIR/.." && pwd)" +ROOT="$(cd "$SMOKE_DIR/../.." && pwd)" + +if ! command -v runtests >/dev/null 2>&1; then + echo "make-references: 'runtests' not found; source scripts/rip-environment first" >&2 + exit 1 +fi + +# GUIs whose tests grab a confirm shot (i.e. run a program). +GUIS=("$@") +if [ "${#GUIS[@]}" -eq 0 ]; then + GUIS=(axis gmoccapy touchy qtdragon) +fi + +export UI_SMOKE_UPDATE_REFERENCE=1 + +rc=0 +for gui in "${GUIS[@]}"; do + dir="$SMOKE_DIR/$gui" + if [ ! -x "$dir/test.sh" ]; then + echo "make-references: no test at $dir, skipping" + continue + fi + echo "=== make-references: $gui ===" + runtests "$dir" || rc=1 + if [ -s "$dir/reference.png" ]; then + echo "make-references: wrote $dir/reference.png" + else + echo "make-references: WARNING no reference.png produced for $gui" >&2 + rc=1 + fi +done + +exit "$rc" diff --git a/tests/ui-smoke/_lib/qtdragon-prepare.sh b/tests/ui-smoke/_lib/qtdragon-prepare.sh index 19741978d73..94818873f10 100644 --- a/tests/ui-smoke/_lib/qtdragon-prepare.sh +++ b/tests/ui-smoke/_lib/qtdragon-prepare.sh @@ -71,6 +71,46 @@ class _BlockFinder(MetaPathFinder): return None sys.meta_path.insert(0, _BlockFinder()) + + +# Native screenshot for the offscreen Qt platform. An X root grab gets a +# black frame (offscreen never draws to the X server), so the launcher +# signals this process instead: on SIGUSR1 we grab the main qtvcp window +# to ui-smoke-qt.png in the cwd (the test dir). The grab is deferred into +# the Qt event loop via singleShot so it never runs in signal context. +import os +import signal + +def _ui_smoke_grab(): + try: + from qtpy.QtWidgets import QApplication + app = QApplication.instance() + if app is None: + return + target, best = None, -1 + for w in app.topLevelWidgets(): + if not w.isVisible(): + continue + area = w.width() * w.height() + if area > best: + target, best = w, area + if target is not None: + out = os.environ.get('UI_SMOKE_QT_SHOT', 'ui-smoke-qt.png') + target.grab().save(out) + except Exception: + pass + +def _ui_smoke_on_sigusr1(signum, frame): + try: + from qtpy.QtCore import QTimer + QTimer.singleShot(0, _ui_smoke_grab) + except Exception: + pass + +try: + signal.signal(signal.SIGUSR1, _ui_smoke_on_sigusr1) +except Exception: + pass PY export PYTHONPATH="$SHIM_DIR${PYTHONPATH:+:$PYTHONPATH}" diff --git a/tests/ui-smoke/_lib/quit-launch.sh b/tests/ui-smoke/_lib/quit-launch.sh index ce6a172f8f7..c374c22e83d 100755 --- a/tests/ui-smoke/_lib/quit-launch.sh +++ b/tests/ui-smoke/_lib/quit-launch.sh @@ -43,10 +43,14 @@ QUIT_GRACE=15 . "$LIB_DIR/crashdump.sh" crashdump_arm +# Absolute path the offscreen-Qt self-grab writes to (the test dir, this +# shell's cwd); see launch.sh for why a relative name would miss. +export UI_SMOKE_QT_SHOT="$PWD/ui-smoke-qt.png" + export CONFIG_INI LIB_DIR DRIVER_TIMEOUT GUI_MATCH QUIT_GRACE # shellcheck disable=SC2016 -xvfb-run -a --server-args="-screen 0 1024x768x24" \ +xvfb-run -a --server-args="-screen 0 $UI_SMOKE_XVFB_SCREEN" \ timeout "$LINUXCNC_TIMEOUT" \ bash -c ' setsid linuxcnc -r "$CONFIG_INI" >linuxcnc.out 2>linuxcnc.err & @@ -95,6 +99,11 @@ xvfb-run -a --server-args="-screen 0 1024x768x24" \ if kill -0 "$GUI_PID" 2>/dev/null; then echo "UI_SMOKE_QUIT_FAIL: GUI (pid $GUI_PID) still alive ${QUIT_GRACE}s after SIGTERM" + # A GUI that absorbs SIGTERM is usually blocked on a modal it + # cannot dismiss headless. Photograph it before teardown so the + # offending dialog is visible. The GUI is still up here. + . "$LIB_DIR/screenshot.sh" + screenshot_grab screenshot.png RC=1 else echo "UI_SMOKE_QUIT_OK: GUI exited ${waited}s after SIGTERM" @@ -126,4 +135,7 @@ echo "=== ui-smoke.err ===" # If the GUI dumped a core, print its native backtrace. crashdump_report +# Note any failure screenshot for the CI artifact step and reviewer. +[ -f screenshot.png ] && echo "=== screenshot: $TEST_DIR/screenshot.png ===" + exit "$RC" diff --git a/tests/ui-smoke/_lib/screenshot.sh b/tests/ui-smoke/_lib/screenshot.sh new file mode 100644 index 00000000000..8088b25bdc5 --- /dev/null +++ b/tests/ui-smoke/_lib/screenshot.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# Screen capture for the UI smoke launchers. Complements crashdump.sh: +# that one fires only on a segfault, but a GUI can also fail by hanging +# (a modal dialog blocking headless startup, a wedged event loop) with no +# core and no Python traceback. A picture of the root window at failure +# time shows what is actually on screen. Source with no state needed; the +# grab is a no-op (with a logged reason) when there is no display or no +# grabber, so it can never turn a pass into a fail. +# +# Must be called from inside the xvfb-run subshell, where DISPLAY points +# at the Xvfb server and the GUI window still exists. CI uploads the PNG +# as a build artifact (see .github/workflows/ci.yml). + +screenshot_grab() { + out="$1" + # Offscreen Qt (qtdragon) renders in memory, never to the X server, so + # an X root grab gets a black frame. Ask the GUI to grab itself + # instead: the qtdragon shim installs a SIGUSR1 handler that saves its + # top-level window to ui-smoke-qt.png in the test dir. + if [ "${QT_QPA_PLATFORM:-}" = "offscreen" ]; then + screenshot_grab_qt "$out" + return 0 + fi + if [ -z "${DISPLAY:-}" ]; then + echo "screenshot: no DISPLAY set, skipping $out" + return 0 + fi + # ImageMagick's import grabs X11 directly with no xwd dependency and + # is the grabber present in the CI image. Fall back to xwd|convert for + # local dev boxes that have xwd but not import. + if command -v import >/dev/null 2>&1; then + if import -window root "$out" 2>/dev/null; then + echo "screenshot: wrote $out" + else + echo "screenshot: import failed for $out" + fi + elif command -v xwd >/dev/null 2>&1 && command -v convert >/dev/null 2>&1; then + if xwd -root -display "$DISPLAY" 2>/dev/null | convert xwd:- "$out" 2>/dev/null; then + echo "screenshot: wrote $out" + else + echo "screenshot: xwd|convert failed for $out" + fi + else + echo "screenshot: no grabber (import or xwd) available, skipping $out" + fi + return 0 +} + +# Native grab for an offscreen Qt GUI. Find the qtvcp python process, +# SIGUSR1 it, and wait for the shim's handler to drop ui-smoke-qt.png in +# the test dir (the GUI's cwd), then move it to $out. No-op (logged) if +# the GUI or the grab is not found, so it can never fail a test. +screenshot_grab_qt() { + out="$1" + # The GUI shim saves to this absolute path (set by the launcher), + # since the offscreen GUI's cwd is the config mirror, not the test dir. + shot="${UI_SMOKE_QT_SHOT:-ui-smoke-qt.png}" + rm -f "$shot" + pid="" + for p in $(pgrep -f "qtvcp" 2>/dev/null); do + arg0=$(tr '\0' '\n' <"/proc/$p/cmdline" 2>/dev/null | head -1) + case "$(basename "$arg0" 2>/dev/null)" in + python*) pid="$p"; break ;; + esac + done + if [ -z "$pid" ]; then + echo "screenshot: qtvcp process not found, skipping $out" + return 0 + fi + kill -USR1 "$pid" 2>/dev/null || true + waited=0 + while [ "$waited" -lt 10 ]; do + [ -s "$shot" ] && break + sleep 0.5 + waited=$((waited + 1)) + done + if [ -s "$shot" ]; then + mv -f "$shot" "$out" + echo "screenshot: wrote $out (qt native grab)" + else + echo "screenshot: qt native grab produced no file for $out" + fi + return 0 +} diff --git a/tests/ui-smoke/axis/reference.png b/tests/ui-smoke/axis/reference.png new file mode 100644 index 00000000000..898a9aab9a5 Binary files /dev/null and b/tests/ui-smoke/axis/reference.png differ diff --git a/tests/ui-smoke/gmoccapy-quit/test.sh b/tests/ui-smoke/gmoccapy-quit/test.sh index 116481f3872..58e77903df1 100755 --- a/tests/ui-smoke/gmoccapy-quit/test.sh +++ b/tests/ui-smoke/gmoccapy-quit/test.sh @@ -1,4 +1,4 @@ #!/bin/bash -exec "$(dirname "$0")/../_lib/quit-launch.sh" \ - "$(cd "$(dirname "$0")/../../../configs/sim" && pwd)/gmoccapy/gmoccapy.ini" \ - "bin/gmoccapy" +LIB_DIR="$(cd "$(dirname "$0")/../_lib" && pwd)" +. "$LIB_DIR/gmoccapy-prepare.sh" +exec "$LIB_DIR/quit-launch.sh" "$GMOCCAPY_INI" "bin/gmoccapy" diff --git a/tests/ui-smoke/gmoccapy/reference.png b/tests/ui-smoke/gmoccapy/reference.png new file mode 100644 index 00000000000..6d2de56f1e0 Binary files /dev/null and b/tests/ui-smoke/gmoccapy/reference.png differ diff --git a/tests/ui-smoke/gmoccapy/test.sh b/tests/ui-smoke/gmoccapy/test.sh index de93beaed99..63dbfee3abe 100755 --- a/tests/ui-smoke/gmoccapy/test.sh +++ b/tests/ui-smoke/gmoccapy/test.sh @@ -1,4 +1,5 @@ #!/bin/bash LIB_DIR="$(cd "$(dirname "$0")/../_lib" && pwd)" -exec "$LIB_DIR/run-gui.sh" gmoccapy/gmoccapy.ini \ +. "$LIB_DIR/gmoccapy-prepare.sh" +exec "$LIB_DIR/run-gui.sh" "$GMOCCAPY_INI" \ --run-program "$LIB_DIR/smoke.ngc" --expect-delta-mm 1,1,0 diff --git a/tests/ui-smoke/qtdragon/reference.png b/tests/ui-smoke/qtdragon/reference.png new file mode 100644 index 00000000000..f15713c3380 Binary files /dev/null and b/tests/ui-smoke/qtdragon/reference.png differ diff --git a/tests/ui-smoke/touchy/reference.png b/tests/ui-smoke/touchy/reference.png new file mode 100644 index 00000000000..196e08487e9 Binary files /dev/null and b/tests/ui-smoke/touchy/reference.png differ