Replace VISUAL-QA.org with structured qa/ test harness

17 categories, each with a README.org checklist and a self-contained
test.el that loads the same scenario in both GPU and vanilla Emacs for
side-by-side comparison.
This commit is contained in:
Andros Fenollosa 2026-06-17 16:07:44 +02:00
parent 8955c39a9b
commit 74a4e6c5cc
36 changed files with 2000 additions and 517 deletions

View file

@ -1,517 +0,0 @@
#+TITLE: Visual QA Checklist — GPU Backend
#+DESCRIPTION: Visual behaviors to verify manually after any change to gfxterm.c, mtlterm.m, glterm.c or gfxdrv.h
#+STARTUP: overview
#+TODO: TODO | PASS FAIL SKIP
* Instructions
Mark each item as PASS, FAIL or SKIP before pushing or releasing.
SKIP = does not apply to the current platform or the specific change being made.
Useful commands to reproduce test cases:
- macOS (Metal): =/tmp/emacs-mtl-buildfull/src/emacs -Q=
- macOS (NS baseline): =EMACS_GPU_DISABLE=1 /tmp/emacs-mtl-buildfull/src/emacs -Q=
- Linux (GPU): =/usr/local/bin/emacs -Q= on the test laptop (ssh debian)
- Linux (cairo baseline): =EMACS_GPU_DISABLE=1 /usr/local/bin/emacs -Q=
General principle: if anything looks different from the NS/cairo baseline
in equivalent cases, it is a failure unless explicitly documented as an
accepted difference.
* 1. Render cycle and present
** TODO 1.1 No black flash on startup
Open without -Q (real config). The window must not show a black or blank
frame before the content is painted.
** TODO 1.2 No flash when switching buffers
=C-x b= between buffers. No previous buffer content or blank frame should
appear for even one vblank.
(Known regression: was the XDBE back buffer on Linux, fixed with
inhibit-double-buffering; on Metal it was swapchain pressure.)
** TODO 1.3 No flash when resizing the window (WM resize)
Drag the window border with the mouse. No black band or displaced text
should appear.
(Regression: v0.4.1 — partial present was caching a stale surface size.)
** TODO 1.4 No flash when opening/closing the minibuffer
=M-x= then =C-g=. The minibuffer area must appear and disappear cleanly.
** TODO 1.5 Clear-only cycle does not present (avoids white flash)
=C-x t t= (tab-bar). The first time it is activated, the tab-bar must
appear without a fully white frame flashing first.
** TODO 1.6 Deferred flush: mouse-face does not flicker
Move the mouse over a link or a mode-line button. The highlight must
appear and disappear without flickering between frames.
** TODO 1.7 Pending scroll-bar clears are applied correctly
Open a long buffer, scroll, then resize. The scroll-bar gutters must not
appear dirty.
* 2. Text and glyphs
** TODO 2.1 Basic ASCII
Open a plain-text ASCII buffer. Characters must be sharp and aligned with
the NS/cairo baseline.
** TODO 2.2 Bold and italic fonts
=(propertize "hello" 'face '(:weight bold))= and =:slant italic=.
Both must be visually distinct from each other and from regular text.
** TODO 2.3 Extended Latin (ñ, ü, é, ç)
Type accented characters. They must not appear vertically shifted
relative to the rest of the line.
** TODO 2.4 CJK (kanji, hangul, Chinese characters)
Open a buffer with CJK text. Characters must occupy twice the width of
ASCII and be centered in their cell.
** TODO 2.5 Symbols and box-drawing (─ │ ┌ ┐ └ ┘)
Open neotree or a buffer with Unicode borders. Lines must connect without
gaps or overlaps.
** TODO 2.6 Font-lock (multiple face colors on the same line)
Open a .el or .py file. Syntax-highlight colors must match the baseline
at the same positions.
** TODO 2.7 Face inheritance (:inherit)
Verify that a face inheriting from another applies its attributes
correctly (color, weight, height).
** TODO 2.8 Relative :height (e.g. :height 1.5)
=(propertize "big" 'face '(:height 1.5))= in a buffer. The text must be
1.5x taller and adjacent lines must not overlap.
** TODO 2.9 Variable-pitch and fixed-pitch on the same line
Enable variable-pitch-mode in a mixed buffer (org-mode with code blocks).
Both fonts must align to the same baseline.
** TODO 2.10 text-scale-increase / text-scale-decrease
=C-x C-= and =C-x C--=. Text must scale cleanly without glyphs from
the previous size remaining visible in the atlas.
** TODO 2.11 Advance precision (no drift on long lines)
Open a line of ~200 characters with wrapping disabled. The cursor at the
end must be exactly at the expected column.
** TODO 2.12 Stroke weight (gamma ^0.82 on Metal)
Compare text in Metal vs NS. Accepted difference: Metal may appear
marginally thinner. Unacceptable: noticeably thicker or thinner than
previous releases.
* 3. Compositions and special scripts
** TODO 3.1 Combining characters (e + ́ = composed é)
Text with combining characters (U+0301 etc.). They must not appear
shifted relative to their base character.
** TODO 3.2 Arabic and Hebrew (shaped, RTL)
Open an Arabic or Hebrew buffer. Text must appear right-to-left with
correctly joined glyphs (proper shaping).
** TODO 3.3 Ligatures (if the font supports them)
With Fira Code or another font with ligatures, verify that -> renders
as a ligated arrow rather than two separate characters.
** TODO 3.4 Automatic composition (auto-composition-mode)
=(setq auto-composition-mode t)= and type text that triggers composition.
Composed glyphs must appear at the correct position.
** TODO 3.5 Mixed BiDi (Arabic + ASCII on the same line)
A buffer with interleaved L2R and R2L text. Verify that the visual
order is correct and cursors move as expected.
* 4. Emoji and color glyphs
** TODO 4.1 Basic emoji (U+1F600, U+1F4BB)
Insert emoji in a buffer. They must appear in color, not grayscale,
and at the correct size (one or two cells).
** TODO 4.2 U+2764 RED HEART (may hang vanilla under Xvfb)
Verify on real hardware only, not in headless Xvfb.
The heart must appear in red.
** TODO 4.3 Emoji in the mode-line
=(setq mode-line-format '("" (:eval "📁 %b")))=
The emoji must render in the mode-line without overlapping adjacent text.
** TODO 4.4 ZWJ sequences (family emoji, flag emoji)
Insert a ZWJ sequence. It must render as a single composed glyph, not
as a sequence of separate characters.
* 5. Face decorations
** TODO 5.1 Single underline
=(propertize "test" 'face '(:underline t))=
One line below the text, aligned to the font's baseline metrics.
** TODO 5.2 Double underline
=(propertize "test" 'face '(:underline (:style double)))=
Two parallel lines below the text.
** TODO 5.3 Wave underline
=(propertize "test" 'face '(:underline (:style wave :color "red")))=
A 3px-tall zigzag line. Verify that it is continuous across adjacent
strings (the phase must not reset per fragment).
** TODO 5.4 Dot underline
=(propertize "test" 'face '(:underline (:style dots)))=
Dotted segments. The phase must be continuous across fragments.
** TODO 5.5 Dash underline
=(propertize "test" 'face '(:underline (:style dashes)))=
Dash segments of 3x the line thickness. Phase must be continuous.
** TODO 5.6 Underline with specific color
=(propertize "test" 'face '(:underline (:color "blue")))=
The underline must be blue; the text must remain in its default color.
** TODO 5.7 Overline
=(propertize "test" 'face '(:overline "green"))=
A green line at the top of the text.
** TODO 5.8 Strike-through
=(propertize "test" 'face '(:strike-through "red"))=
A red line at mid-height of the text.
** TODO 5.9 Underline clamped to descent
Text with a font that has a small descent. The underline must not
escape the line cell. If pos+thickness > descent, it must collapse
to 1px at descent-1.
* 6. Boxes and reliefs
** TODO 6.1 Simple box outline
=(propertize "test" 'face '(:box 1))=
A 1px border around the text.
** TODO 6.2 Box with larger line width
=(propertize "test" 'face '(:box (:line-width 3 :color "blue")))=
A blue 3px border. Horizontal and vertical edges must have the correct
thickness.
** TODO 6.3 Raised relief (mode-line, buttons)
Activate the mode-line and verify that tab-bar buttons appear raised
(light border top/left, dark border bottom/right).
** TODO 6.4 Sunken relief
=(propertize "test" 'face '(:box (:line-width 2 :style sunken-box)))=
The border must appear sunken (dark top/left, light bottom/right).
** TODO 6.5 Relief corners must not appear light
On a raised relief of >= 2px, corners where the light and shadow edges
overlap must appear dark (corner darkening).
** TODO 6.6 Relief colors in dark mode (Metal/macOS)
Switch to a dark theme. The mode-line relief colors must remain
coherent (not inverted or broken).
** TODO 6.7 Relief on images (:relief in tool-bar)
Verify that tool-bar buttons have correct relief (raised at rest,
sunken when pressed if applicable).
* 7. Cursor
** TODO 7.1 FILLED_BOX (default cursor)
The filled box cursor must have inverted colors: cursor background
color, text in the cursor background color.
** TODO 7.2 HOLLOW_BOX
=(setq cursor-type 'hollow)=
Four 1px edges around the cell, transparent interior.
** TODO 7.3 BAR (vertical bar cursor)
=(setq cursor-type 'bar)=
A 1-2px vertical bar on the left edge of the cell.
For R2L text: on the right edge.
** TODO 7.4 HBAR (horizontal bar cursor)
=(setq cursor-type 'hbar)=
A horizontal bar at the bottom of the cell.
** TODO 7.5 Cursor on fringe when no glyph is present
Move the cursor to an empty line at the end of the buffer.
The cursor must appear in the fringe, not disappear.
** TODO 7.6 Cursor adapts to text-scale
Change text-scale and verify that the cursor adapts to the new cell
size.
** TODO 7.7 Invisible cursor (blink off state)
With blink-cursor-mode active, verify that during the "off" phase
the cursor disappears completely.
** TODO 7.8 [Metal] BLOCK cursor — no animation effect
=(setq gpu-cursor-animation 'block)=
The cursor must be static with no trail or animation when moving.
** TODO 7.9 [Metal] SPRING cursor
=(setq gpu-cursor-animation 'spring)=
The cursor must slide to the new position with critical damping
(~150ms settling time).
** TODO 7.10 [Metal] TORPEDO cursor (trail)
=(setq gpu-cursor-animation 'torpedo)=
Moving the cursor quickly must leave a fading trail behind it.
** TODO 7.11 [Metal] SONICBOOM, RIPPLE, PIXIEDUST effects on large jumps
Jump with =M-g g= to a distant line. Verify the burst/ripple/dust
effect appears around the destination.
** TODO 7.12 [Metal] Effect suppression during typing
=(setq gpu-cursor-effects-while-typing nil)=
Typing text rapidly must not trigger cursor animation effects on
every character insertion.
* 8. Fringe
** TODO 8.1 Line continuation arrows
Enable truncate-lines and create a very long line. Continuation
arrows must appear in the right fringe.
** TODO 8.2 Empty-line indicator
Move past the last line of a buffer with trailing empty lines.
The tilde indicator must appear in the left fringe.
** TODO 8.3 Truncation arrows (left/right)
In a buffer with hscroll, truncation arrows must appear in the
appropriate fringe on each side.
** TODO 8.4 Custom fringe bitmap
=(define-fringe-bitmap 'my-bm [8 4 2 1 2 4 8] nil nil 'center)=
The bitmap must render at the correct position and color.
** TODO 8.5 Fringe background color
=(set-face-background 'fringe "yellow")=
The fringe area must have a yellow background.
** TODO 8.6 Fringe bitmap does not bleed onto the mode-line
With a long buffer, scroll to the end. The 72px empty-line indicator
must not overlap the mode-line.
** TODO 8.7 Overlay bitmap does not erase underlying content
Bitmaps with the overlay flag must be drawn on top of existing content
without clearing the fringe background first.
* 9. Images
** TODO 9.1 Opaque PNG
=(insert-image (create-image "/path/to/opaque.png"))=
The image must appear with correct colors.
** TODO 9.2 PNG with alpha channel
A PNG image with transparency. The Emacs background must show through
the transparent areas.
** TODO 9.3 JPEG
A JPEG image must appear without compression artifacts added by the
backend.
** TODO 9.4 SVG
=(insert-image (create-image "/path/to/image.svg" 'svg))=
The vector image must render sharply.
** TODO 9.5 Static GIF (first frame)
A static GIF must display correctly (the backend always shows the first
frame in image-mode).
** TODO 9.6 Image with :scale
=(create-image ... :scale 2.0)=
The image must appear twice as large as the original.
** TODO 9.7 Image with :rotation
=(create-image ... :rotation 90)=
The image must appear rotated 90 degrees.
** TODO 9.8 Image slice (UV subrect)
=(insert-image img nil nil '(0 0 50 50))=
Only the specified crop of the image must be visible.
** TODO 9.9 Image with :relief
=(create-image ... :relief 2)=
The image must have a raised-relief border around it.
** TODO 9.10 Image with :margin
The image must have correct spacing relative to adjacent text.
** TODO 9.11 Image cache (same image inserted twice)
Insert the same image in two different places in the buffer. Both
occurrences must be identical.
** TODO 9.12 Inline images (iimage-mode)
=(iimage-mode)= in a directory with referenced images.
Inline images must render correctly.
* 10. Scrolling and copy_region
** TODO 10.1 Smooth scroll down (C-n, C-v)
Existing text must slide upward without artifacts. New content must
appear cleanly at the bottom.
** TODO 10.2 Smooth scroll up (C-p, M-v)
Same as above but in the opposite direction.
** TODO 10.3 scroll_run: pixel-perfect parity with NS/cairo
The result of scrolling N lines must be identical to a fresh redraw
of the same lines. (See the run-scroll.sh harness.)
** TODO 10.4 Overlapping copy_region (scroll with overlap)
A 1-line scroll where src and dst overlap. Content must move correctly
(bounced through the scratch texture).
** TODO 10.5 [Metal] Scroll with EASE_OUT_QUAD easing
=(setq gpu-scroll-easing 'ease-out-quad)=
The scroll must show a smooth deceleration at the end.
** TODO 10.6 [Metal] Scroll with SPRING easing
=(setq gpu-scroll-easing 'spring)=
The scroll must slightly overshoot and bounce back.
** TODO 10.7 [Metal] Instant scroll (no easing)
=(setq gpu-scroll-easing 'none)=
The scroll must be instant, matching the baseline.
** TODO 10.8 Horizontal scroll (hscroll)
A long line with truncate-lines. =C-e= to jump to the end.
Content must scroll horizontally without artifacts.
** TODO 10.9 Glyph shift on insert (gfx_shift_glyphs_for_insert)
With overwrite-mode disabled, insert a character in the middle of a
line. Text to the right must shift cleanly.
* 11. Emacs bars
** TODO 11.1 Basic mode-line
The mode-line must show the buffer name, major mode and line/column
info with correct foreground and background colors.
** TODO 11.2 Mode-line with custom face
=(set-face-background 'mode-line "navy")=
The mode-line must have a navy-blue background.
** TODO 11.3 Inactive mode-line (another window)
The mode-line of inactive windows must use mode-line-inactive face.
** TODO 11.4 Header-line
=(setq header-line-format "Header")=
A bar must appear above the text area.
** TODO 11.5 Tab-line (per-window)
=(global-tab-line-mode)=
Per-window tabs must render with relief and hover highlighting.
** TODO 11.6 Tab-bar (per-frame)
=(tab-bar-mode)=
Per-frame tabs must appear at the top without flickering when
switching tabs.
** TODO 11.7 Tool-bar
=(tool-bar-mode)=
Tool-bar icons must have raised relief and respond to hover without
flickering.
** TODO 11.8 Scroll-bar
=(scroll-bar-mode)=
The scroll-bar must appear on the right and update its position when
scrolling.
* 12. Windows and splits
** TODO 12.1 Vertical split (C-x 2)
Two stacked windows. The dividing line must be visible and the
content of each window must not overlap.
** TODO 12.2 Horizontal split (C-x 3)
Two side-by-side windows. The vertical divider must be visible.
** TODO 12.3 Window divider >= 3px (first/last pixel colors)
=(setq window-divider-default-bottom-width 5)=
A 5px divider must have distinct colors on the first and last pixel.
** TODO 12.4 Resize windows with C-x ^, C-x {, C-x }
Windows must resize cleanly without rendering artifacts.
** TODO 12.5 Internal frame border (internal-border-width)
=(set-frame-parameter nil 'internal-border-width 10)=
A 10px border must appear with the correct color.
** TODO 12.6 Multiple frames (make-frame)
Create a second frame. Both frames must render independently without
interfering with each other.
** TODO 12.7 Child frame (corfu, company popup)
Open a completion popup. The child frame must appear above the text
without artifacts on its edges.
* 13. Display properties and overlays
** TODO 13.1 Invisible text
=(put-text-property 1 5 'invisible t)=
Invisible text must not appear on screen but the cursor must skip
over it correctly.
** TODO 13.2 Display string (replacing text)
=(put-text-property 1 5 'display "REPLACED")=
The original text must be replaced by "REPLACED".
** TODO 13.3 Inline image via display property
=(put-text-property 1 2 'display (create-image ...))=
The image must appear in place of the character.
** TODO 13.4 Display space (:width, :align-to)
=(put-text-property 1 2 'display '(space :width 10))=
A space of 10 columns must appear.
** TODO 13.5 Overlay with face
=(overlay-put (make-overlay 1 10) 'face '(:background "yellow"))=
The overlay text must have a yellow background.
** TODO 13.6 Overlay with after-string / before-string
The extra string must appear at the correct position without
unexpectedly displacing surrounding text.
** TODO 13.7 Line numbers (display-line-numbers-mode)
=(display-line-numbers-mode)=
Line numbers must appear right-aligned in the left margin.
** TODO 13.8 hl-line-mode
=(hl-line-mode)=
The cursor line must have a distinct highlight background.
** TODO 13.9 Margins (left-margin-width, right-margin-width)
=(setq left-margin-width 4)=
A 4-column blank left margin must appear.
** TODO 13.10 Line wrapping (word-wrap, visual-line-mode)
Long lines that wrap. Wrapping must occur at word boundaries and
continuation lines must be correctly indented.
** TODO 13.11 Truncate lines
=(setq truncate-lines t)=
Long lines must be cut with a truncation arrow in the fringe.
* 14. Common major modes (real-world integration)
** TODO 14.1 org-mode with headings, code blocks and tables
Open a real .org file. Headings with colors, code blocks with
background, tables with lines. No artifacts.
** TODO 14.2 dired
=(dired "~")= List a directory. File names, permissions and dates
must align correctly.
** TODO 14.3 magit (git interface)
Open magit-status. Diff hunks with add/remove colors, foldable
sections. No flickering when expanding/collapsing.
** TODO 14.4 corfu / company (completion popup)
In a code buffer, trigger completion. The popup (child frame or
overlay) must appear without flickering and with correct highlight
on the selected candidate.
** TODO 14.5 vertico / ivy / helm (minibuffer with candidates)
Open =M-x= with vertico. The candidate list must render cleanly
with the current item highlighted.
** TODO 14.6 elfeed (RSS reader with links and mixed fonts)
Open an article in elfeed. Links must have underlines and paragraph
font must be correct.
** TODO 14.7 treemacs / neotree (file tree with icons)
Open the file tree. Unicode/emoji icons must render at the correct
column.
* 15. HiDPI / Retina
** TODO 15.1 [macOS] Retina @2x: atlas rasterized at physical resolution
On a Retina display, open the app and capture with gpu-capture-frame.
The capture size must be exactly double the logical size.
** TODO 15.2 [macOS] Text sharpness @2x
Zoom in 6x on the capture. Glyphs must show per-physical-pixel
antialiasing, not blurry upscaling from @1x.
** TODO 15.3 [macOS] NS vs Metal parity @2x
Compare Metal vs NS capture (EMACS_GPU_DISABLE=1) with the harness.
fuzz5%=~0.5%, fuzz20%=~0.05% are acceptable (AA on glyph edges).
** TODO 15.4 [Linux] GL_SCALE > 1 (supersampling)
=GL_SCALE=2 emacs -Q=
Text must appear sharper than at scale 1x.
* 16. Buffer-switch transition (crossfade)
** TODO 16.1 Crossfade when switching buffers
=(setq gpu-buffer-transitions t)=
On =C-x b=, the previous buffer must fade out smoothly as the
new content appears.
** TODO 16.2 Configurable duration
=(setq gpu-buffer-transition-duration 0.5)=
The crossfade must last approximately 0.5 seconds.
** TODO 16.3 No artifacts after the fade
When the fade ends, the new buffer must appear perfectly clean,
with no residue from the previous buffer.
** TODO 16.4 Pump cadence during fade (60Hz)
The fade must be smooth (~60fps). It must not look like 30fps or
show frame jumps.
* 17. Inline video (macOS Metal only)
** TODO 17.1 Insert video with gpu-video-insert
=(gpu-video-insert "/path/to/video.mp4")=
A placeholder must appear in the buffer.
** TODO 17.2 Playback: video appears at the placeholder position
When playback starts, the video must appear over the placeholder,
not at another position in the frame.
** TODO 17.3 Video follows scrolling
Scroll the buffer while the video is playing. The video must move
with the text, not remain fixed on screen.
** TODO 17.4 Video clipped to window interior
The video must not bleed over the mode-line or other windows.
** TODO 17.5 Stop with gpu-video-stop
The placeholder must remain and the video must stop playing.
** TODO 17.6 Stable 30fps cadence (no jumps, no timer starvation)
The median interval between frames must be ~33ms with p90 < 40ms.
* 18. GL backend / Linux (platform-specific)
** TODO 18.1 Partial present (EGL_EXT_buffer_age)
Scroll in a large buffer. Only the changed region must be submitted
(verify with GL_LOG_PRESENT=1 that damage is partial).
** TODO 18.2 No flash from stale XDBE buffers
=inhibit-double-buffering= must be set for GL frames. No startup
content should appear during buffer transitions.
** TODO 18.3 Scroll parity: cairo vs GPU
Run run-bench.sh and compare page-scroll and line-scroll FPS against
the cairo baseline. page-scroll must be at parity (1.0x),
full-redraw > 1.0x.
** TODO 18.4 Resize under Xvfb without black bands
Resize the window under Xvfb + openbox. The resized frame must not
show black bands or text from the previous size.
** TODO 18.5 buffer-age == 0: re-query surface size
After a resize, the first frame with age==0 must use the new size,
not the cached stale size.
** TODO 18.6 EGL surfaceless headless (GL_FORCE_SURFACELESS=1)
The comparison harness under Xvfb must still produce the correct
FBO output and acceptable diffs.
* 19. Edge cases and performance
** TODO 19.1 Buffer with 100k lines
Open a large file (logs). Scrolling must be fluid with no perceptible
lag.
** TODO 19.2 Many simultaneous overlays (hl-line + multiple packages)
With hl-line, doom-modeline, corfu and company all active, verify
there is no flickering in the mode-line or windows.
** TODO 19.3 Theme change (M-x load-theme)
Switch from a light to a dark theme. All colors must update without
artifacts or remnants of the previous theme.
** TODO 19.4 Atlas overflow (glyph atlas reset)
Use many different fonts and sizes until the atlas fills and resets.
Text must continue to display correctly after the reset.
** TODO 19.5 Garbaged frame (iconify and restore)
Iconify and then restore the window. Content must be fully and
correctly redrawn.
** TODO 19.6 MTL_LOG_SEQ=1 — no critical errors
=MTL_LOG_SEQ=1 emacs -Q= and perform normal operations. Logs must
not show unexpected "outside cycle" entries or presents without a
corresponding draw.
* 20. Configuration and knobs
** TODO 20.1 gpu-status reports the active backend
=(gpu-status)= must report "Metal" or "OpenGL" according to the
platform.
** TODO 20.2 EMACS_GPU_DISABLE=1 disables the backend
Start with that env var. Rendering must be identical to vanilla Emacs
(NS on macOS, cairo on Linux).
** TODO 20.3 gpu-enable-for-frame / gpu-disable-for-frame
Disable GPU rendering on a specific frame. That frame must use the
native renderer while others remain on GPU.
** TODO 20.4 gpu-capture-frame (macOS)
=(gpu-capture-frame "/tmp/capture.png")=
The capture must match what is visible on screen.
** TODO 20.5 Warm glyph cache on startup (no blink on first draw)
Open Emacs and switch tabs immediately. The first redraw must not
show a blank frame while ASCII is being rasterized into the atlas.
* Platform notes
- Items marked [Metal] apply only on macOS with the Metal driver.
- Items marked [Linux] or [GL] apply only with the OpenGL/EGL driver.
- Unmarked items must be verified on both platforms.
- The comparison baseline is always: same binary with EMACS_GPU_DISABLE=1.

View file

@ -0,0 +1,38 @@
#+TITLE: QA 01 — Render Cycle & Present
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/01-render-cycle/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/01-render-cycle/test.el
Follow the interactive steps printed in the =*QA: Render Cycle*= buffer.
* Checklist
** TODO 1.1 No black flash on startup
The window must be fully painted before it becomes visible.
Failure: a black or white frame appears for 12 vblanks then snaps to content.
** TODO 1.2 No content flash when switching buffers
Press =C-x b= to go to =*scratch*=, then back.
Failure: the previous buffer's content flickers in for one frame.
** TODO 1.3 No black band on WM resize
Drag the window border with the mouse.
Failure: a black or garbled band appears at the trailing edge during resize.
** TODO 1.4 No flash when toggling tab-bar
Press =C-x t t= to toggle the tab-bar.
Failure: a fully white frame flashes before the tab-bar appears.
** TODO 1.5 No flash when opening / closing the minibuffer
Press =M-x= then =C-g=.
Failure: the area below the last window flickers white or shows garbage.
** TODO 1.6 Mouse-face does not flicker
Move the mouse slowly over the mode-line buttons or links in the buffer.
Failure: highlight appears and immediately disappears for one frame before settling.
** TODO 1.7 Iconify and restore
Minimize the window (=C-z= or WM title bar) and restore it.
Failure: content is not redrawn correctly after restore.

View file

@ -0,0 +1,29 @@
;;; test.el --- Visual QA: Render Cycle & Present -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/01-render-cycle/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/01-render-cycle/test.el
(let ((buf (get-buffer-create "*QA: Render Cycle*")))
(with-current-buffer buf
(erase-buffer)
(insert (propertize "=== Render Cycle & Present QA ===\n\n"
'face '(:weight bold :height 1.3)))
(insert "Perform each step below and check for visual artifacts.\n\n")
(insert (propertize "Steps:\n" 'face '(:weight bold)))
(insert " 1. Note: did this window appear cleanly? (no black flash on startup)\n")
(insert " 2. C-x t t — toggle tab-bar (must not flash white)\n")
(insert " 3. M-x C-g — open and close minibuffer (no flash below text area)\n")
(insert " 4. Drag the window border with the mouse (no black band during resize)\n")
(insert " 5. C-x b RET — switch to *scratch* then back (no old content flicker)\n")
(insert " 6. Move mouse over mode-line buttons (mouse-face must not flicker)\n")
(insert " 7. Minimize and restore the window (content must redraw correctly)\n\n")
(insert (make-string 65 ?─) "\n")
(insert "Background content (to make content flicker visible):\n\n")
(dotimes (i 30)
(insert (format " Line %3d │ The quick brown fox jumps over the lazy dog 1234567890\n" i)))
(goto-char (point-min)))
(switch-to-buffer buf)
(setq buffer-read-only t))

View file

@ -0,0 +1,45 @@
#+TITLE: QA 02 — Text & Glyphs
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/02-text-glyphs/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/02-text-glyphs/test.el
* Checklist
** TODO 2.1 ASCII — sharp and baseline-aligned
All ASCII characters must be crisp and sit on the same baseline.
Failure: blurry glyphs, or characters shifted up/down relative to neighbors.
** TODO 2.2 Bold and italic are visually distinct
The bold line must have heavier strokes than regular; italic must slant.
Failure: bold looks identical to regular, or italic does not slant.
** TODO 2.3 Extended Latin — no vertical shift
Accented characters (ñ, ü, é, ç...) must align to the same baseline as ASCII.
Failure: accented characters sit 12px higher or lower than unaccented ones.
** TODO 2.4 CJK — double-width, centered
CJK characters must occupy exactly twice the width of an ASCII cell and
be horizontally centered within that space.
Failure: CJK characters overlap ASCII, or are misaligned.
** TODO 2.5 Box-drawing — connected lines
Borders must form clean connected rectangles without gaps or overlaps at corners.
Failure: a 1px gap appears at T-junctions or corners.
** TODO 2.6 Font-lock colors — correct per face
Each face color (keyword, string, comment...) must match the baseline.
Failure: a face renders in the wrong color or falls back to the default.
** TODO 2.7 :height scaling — no line overlap
The 1.5× line must be taller, but lines must not overlap each other.
Failure: the tall line bleeds into the adjacent line above or below.
** TODO 2.8 Advance precision — no drift
On the long 200-character line, the cursor at the end must be at the
correct column. Failure: cursor is offset by 12px from expected position.
* Accepted differences
- Metal text may appear marginally thinner than NS (gamma ^0.82 correction).
This is by design and not a failure.

78
qa/02-text-glyphs/test.el Normal file
View file

@ -0,0 +1,78 @@
;;; test.el --- Visual QA: Text & Glyphs -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/02-text-glyphs/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/02-text-glyphs/test.el
(let ((buf (get-buffer-create "*QA: Text & Glyphs*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(insert (propertize "=== Text & Glyphs QA ===\n\n" 'face '(:weight bold :height 1.3)))
;; ASCII + weights
(insert (propertize "── ASCII & weights " 'face 'shadow)
(make-string 45 ?─) "\n")
(insert "Regular: The quick brown fox jumps over the lazy dog 0123456789\n")
(insert (propertize "Bold: The quick brown fox jumps over the lazy dog 0123456789\n"
'face '(:weight bold)))
(insert (propertize "Italic: The quick brown fox jumps over the lazy dog 0123456789\n"
'face '(:slant italic)))
(insert (propertize "Bold+Ital: The quick brown fox jumps over the lazy dog 0123456789\n"
'face '(:weight bold :slant italic)))
(insert "\n")
;; Latin extended
(insert (propertize "── Latin extended " 'face 'shadow)
(make-string 47 ?─) "\n")
(insert "Lower: àáâãäåæçèéêëìíîïðñòóôõöùúûüýþÿ\n")
(insert "Upper: ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝÞŸ\n\n")
;; CJK
(insert (propertize "── CJK " 'face 'shadow)
(make-string 57 ?─) "\n")
(insert "日本語: 日本語のテキスト、漢字、ひらがな、カタカナ\n")
(insert "中文: 中文文字测试,简体字与繁體字混合\n")
(insert "한국어: 한글 텍스트 테스트 — Hangul\n\n")
;; Height
(insert (propertize "── :height face attribute " 'face 'shadow)
(make-string 38 ?─) "\n")
(insert (propertize "0.75× small " 'face '(:height 0.75)))
(insert "1.00× normal ")
(insert (propertize "1.25× large " 'face '(:height 1.25)))
(insert (propertize "1.50× larger\n" 'face '(:height 1.5)))
(insert "\n")
;; Box-drawing
(insert (propertize "── Box-drawing characters " 'face 'shadow)
(make-string 38 ?─) "\n")
(insert "┌────────────┬────────────┬────────────┐\n")
(insert "│ Cell A │ Cell B │ Cell C │\n")
(insert "├────────────┼────────────┼────────────┤\n")
(insert "│ Cell D │ Cell E │ Cell F │\n")
(insert "└────────────┴────────────┴────────────┘\n\n")
;; Font-lock faces
(insert (propertize "── Font-lock face colors " 'face 'shadow)
(make-string 39 ?─) "\n")
(insert (propertize "keyword " 'face 'font-lock-keyword-face))
(insert (propertize "builtin " 'face 'font-lock-builtin-face))
(insert (propertize "function " 'face 'font-lock-function-name-face))
(insert (propertize "variable " 'face 'font-lock-variable-name-face))
(insert "\n")
(insert (propertize "string " 'face 'font-lock-string-face))
(insert (propertize "comment " 'face 'font-lock-comment-face))
(insert (propertize "constant " 'face 'font-lock-constant-face))
(insert (propertize "type " 'face 'font-lock-type-face))
(insert "\n\n")
;; Advance precision
(insert (propertize "── Advance precision (cursor at end must be at correct column) " 'face 'shadow)
"\n")
(insert (make-string 200 ?x))
(insert "\n")
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf))

View file

@ -0,0 +1,30 @@
#+TITLE: QA 03 — Compositions & Special Scripts
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/03-compositions/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/03-compositions/test.el
* Checklist
** TODO 3.1 Combining characters — identical to precomposed
The "Combined" line must look identical to the "Composed" line above it.
Failure: diacritics are offset, duplicated, or missing.
** TODO 3.2 Arabic — RTL, correctly shaped and joined
Arabic text must flow right-to-left with letters joined at appropriate
contextual forms (initial/medial/final/isolated).
Failure: letters are in the wrong order, or appear in isolated form only.
** TODO 3.3 Hebrew — RTL, correct character order
Hebrew text must flow right-to-left.
Failure: characters appear left-to-right, or vowel marks are misplaced.
** TODO 3.4 Mixed BiDi — correct visual order on the same line
In the mixed line, the English and Arabic portions must each be in their
correct visual direction and positioned relative to each other correctly.
Failure: portions are in the wrong visual order or overlap.
** TODO 3.5 Devanagari — consonant clusters formed correctly
The halanta (virama) must produce conjunct forms (e.g., nma → न्म).
Failure: conjuncts do not form; characters appear separately.

View file

@ -0,0 +1,59 @@
;;; test.el --- Visual QA: Compositions & Special Scripts -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/03-compositions/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/03-compositions/test.el
(let ((buf (get-buffer-create "*QA: Compositions*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(insert (propertize "=== Compositions & Special Scripts QA ===\n\n"
'face '(:weight bold :height 1.3)))
;; Combining characters
(insert (propertize "── Combining characters (both lines must look identical) " 'face 'shadow)
(make-string 8 ?─) "\n")
(insert "Precomposed: café naïve résumé Ångström crème brûlée\n")
;; Same strings built from base + combining codepoints
(insert (concat "Combined: café"
" naïve"
" résumé"
" A̋ngström"
" crème brûlée\n\n"))
;; Arabic
(insert (propertize "── Arabic — right-to-left, joined glyphs " 'face 'shadow)
(make-string 22 ?─) "\n")
(insert "بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ\n")
(insert "مرحبًا بالعالم — Hello World in Arabic\n\n")
;; Hebrew
(insert (propertize "── Hebrew — right-to-left " 'face 'shadow)
(make-string 38 ?─) "\n")
(insert "שָׁלוֹם עוֹלָם — Shalom Olam (Hello World)\n")
(insert "אבגדהוזחטיכלמנסעפצקרשת\n\n")
;; Mixed BiDi
(insert (propertize "── Mixed BiDi — English + Arabic on same line " 'face 'shadow)
(make-string 17 ?─) "\n")
(insert "English left, ثُمَّ Arabic, then English again.\n")
(insert "Item 1: سلام — Item 2: hello — Item 3: مرحبا\n\n")
;; Devanagari
(insert (propertize "── Devanagari — conjunct forms " 'face 'shadow)
(make-string 32 ?─) "\n")
(insert "नमस्ते दुनिया — Namaste (Hindi)\n")
(insert "संस्कृतम् — Sanskrit\n")
(insert "क्ष त्र ज्ञ (conjunct consonants)\n\n")
;; Greek
(insert (propertize "── Greek " 'face 'shadow)
(make-string 55 ?─) "\n")
(insert "αβγδεζηθικλμνξοπρστυφχψω — lower\n")
(insert "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ — upper\n")
(insert "Ελληνικά — Greek (language name)\n\n")
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf))

37
qa/04-emoji/README.org Normal file
View file

@ -0,0 +1,37 @@
#+TITLE: QA 04 — Emoji & Color Glyphs
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/04-emoji/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/04-emoji/test.el
* Checklist
** TODO 4.1 Basic emoji — rendered in color
Face/animal/food emoji must appear in full color, not grayscale.
Failure: emoji rendered as black outlines or as replacement boxes.
** TODO 4.2 Emoji width — occupies two columns
Wide emoji (🎸, 🌍...) must each take exactly two character-cell widths.
Failure: emoji overlap the next character, or leave a blank gap.
** TODO 4.3 ❤️ and other symbol emoji — in color
U+2764 (RED HEART) with VS16 must appear red, not as a text symbol.
Failure: appears as a black ♥ shape.
** TODO 4.4 Emoji in mixed text — no displacement
In the "mixed text" lines, text before and after emoji must align correctly.
Failure: text after the emoji is shifted left or right.
** TODO 4.5 ZWJ sequences — rendered as single glyph
Family / profession emoji (👨‍💻, 👩‍🎤...) must render as one combined glyph,
not as two or three separate emoji side by side.
Failure: the component emoji are visible separately.
** TODO 4.6 Flag emoji — correct flag shown
Country flags (🇺🇸, 🇩🇪...) must appear as the correct national flag image.
Failure: letters "US", "DE"... appear instead of flags, or wrong flags shown.
** TODO 4.7 Emoji with :height face — scales correctly
The 2.0× emoji must be visibly larger than the normal-size ones.
Failure: size is unchanged, or the glyph is clipped.

53
qa/04-emoji/test.el Normal file
View file

@ -0,0 +1,53 @@
;;; test.el --- Visual QA: Emoji & Color Glyphs -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/04-emoji/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/04-emoji/test.el
(let ((buf (get-buffer-create "*QA: Emoji & Color Glyphs*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(insert (propertize "=== Emoji & Color Glyphs QA ===\n\n"
'face '(:weight bold :height 1.3)))
(insert (propertize "── Basic emoji (must appear in color) " 'face 'shadow)
(make-string 26 ?─) "\n")
(insert "Faces: 😀 😁 😂 🤣 😃 😄 😅 😆 😉 😊 😋 😎 🤩 🥳\n")
(insert "Animals: 🐶 🐱 🐭 🐹 🐰 🦊 🐻 🐼 🐨 🐯 🦁 🐮 🐷 🐸\n")
(insert "Food: 🍎 🍊 🍋 🍇 🍓 🍒 🍑 🥭 🍍 🥥 🍆 🥦 🥕 🌽\n\n")
(insert (propertize "── Symbol emoji — must be in color (with VS16) " 'face 'shadow)
(make-string 16 ?─) "\n")
(insert "❤️ 💙 💚 💛 💜 🖤 🤍 🤎\n")
(insert "⭐ 🌟 💫 ✨ 🌈 ☀️ 🌙 ⚡ ☔ ❄️\n\n")
(insert (propertize "── Wide emoji — each occupies two columns " 'face 'shadow)
(make-string 22 ?─) "\n")
(insert "🎯 🎨 🎭 🎪 🎤 🎧 🎵 🎶 🎸 🎹 🥁 🎺 🎻 🪕\n")
(insert "🌍 🌎 🌏 🗺️ 🧭 🏔️ 🌋 🏕️ 🏖️ 🏜️ 🏝️\n\n")
(insert (propertize "── Emoji in mixed text — text must not shift " 'face 'shadow)
(make-string 19 ?─) "\n")
(insert "File 📁 opened — status: ✅ — errors: ❌ — warnings: ⚠️\n")
(insert "Mode 🔴 stopped | 🟡 paused | 🟢 running\n")
(insert "Score: 💯 Rank: 🥇 Time: ⏰ Memory: 💾\n\n")
(insert (propertize "── ZWJ sequences — must render as a single glyph " 'face 'shadow)
(make-string 13 ?─) "\n")
(insert "👨‍💻 👩‍💻 👨‍🎨 👩‍🎤 👨‍🍳 👩‍🚀 👨‍🔬 👩‍🏫\n\n")
(insert (propertize "── Flag emoji " 'face 'shadow)
(make-string 50 ?─) "\n")
(insert "🇺🇸 🇬🇧 🇩🇪 🇫🇷 🇯🇵 🇰🇷 🇨🇳 🇪🇸 🇧🇷 🇮🇳 🇲🇽 🇮🇹\n\n")
(insert (propertize "── Emoji with face attributes " 'face 'shadow)
(make-string 33 ?─) "\n")
(insert "Normal: 🔥 ")
(insert (propertize "Bold: 🔥 " 'face '(:weight bold)))
(insert (propertize "2.0×: 🌍" 'face '(:height 2.0)))
(insert "\n")
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf))

View file

@ -0,0 +1,54 @@
#+TITLE: QA 05 — Face Decorations
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/05-decorations/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/05-decorations/test.el
* Checklist
** TODO 5.1 Single underline — below baseline, correct color
A single horizontal line must appear below the text, touching but not
overlapping the descenders.
Failure: line is missing, is above the text, or uses the wrong color.
** TODO 5.2 Double underline — two parallel lines
Two horizontal lines must appear, equally spaced, below the text.
Failure: only one line visible, or lines overlap.
** TODO 5.3 Wave underline — continuous zigzag, 3px tall
The zigzag pattern must be continuous across the entire string.
The wave must NOT restart at glyph-string boundaries mid-word.
Failure: the wave resets (abrupt phase jump) at word or face boundaries.
** TODO 5.4 Dot underline — dotted segments, continuous phase
Dots must be evenly spaced and the pattern must continue unbroken
across face-change boundaries.
Failure: dots cluster or reset at boundaries.
** TODO 5.5 Dash underline — dash segments, continuous phase
Dashes of 3× the stroke thickness, evenly spaced.
Failure: dashes reset at boundaries or have inconsistent spacing.
** TODO 5.6 Underline color independent of text color
When the face specifies both a text color and an underline color, each
must be applied to the correct element.
Failure: underline inherits text color when it should not.
** TODO 5.7 Overline — 1px at the top of the text
A single pixel line at the top of the glyph bounding box.
Failure: line appears at the bottom, or is missing entirely.
** TODO 5.8 Strike-through — centered vertically
A horizontal line at approximately the mid-height of the glyph body.
Failure: line is at the top, bottom, or is missing.
** TODO 5.9 Decorations combined — no interference
When underline + overline + strike-through are all applied, all three
must be visible simultaneously without any canceling each other out.
Failure: one or more decorations disappear when combined.
** TODO 5.10 Wave continuity across face boundaries
The "continuous" wave line must have no phase jump where the bold
fragment meets the regular-weight fragments.
Failure: a visible step or gap in the wave at the bold boundary.

75
qa/05-decorations/test.el Normal file
View file

@ -0,0 +1,75 @@
;;; test.el --- Visual QA: Face Decorations -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/05-decorations/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/05-decorations/test.el
(let ((buf (get-buffer-create "*QA: Decorations*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(insert (propertize "=== Face Decorations QA ===\n\n"
'face '(:weight bold :height 1.3)))
;; Underlines
(insert (propertize "── Underline styles " 'face 'shadow) (make-string 44 ?─) "\n")
(insert (propertize "Single (default color) — The quick brown fox\n"
'face '(:underline t)))
(insert (propertize "Single (cornflower blue) — The quick brown fox\n"
'face '(:underline (:color "cornflower blue"))))
(insert (propertize "Double (default color) — The quick brown fox\n"
'face '(:underline (:style double))))
(insert (propertize "Double (red) — The quick brown fox\n"
'face '(:underline (:style double :color "red"))))
(insert (propertize "Wave (orange) — The quick brown fox\n"
'face '(:underline (:style wave :color "orange"))))
(insert (propertize "Dots (default color) — The quick brown fox\n"
'face '(:underline (:style dots))))
(insert (propertize "Dashes (default color) — The quick brown fox\n"
'face '(:underline (:style dashes))))
(insert "\n")
;; Continuity across face boundaries
(insert (propertize "── Wave continuity across face changes (must not reset) " 'face 'shadow)
(make-string 7 ?─) "\n")
(insert (propertize "This is " 'face '(:underline (:style wave :color "red"))))
(insert (propertize "one continuous " 'face '(:underline (:style wave :color "red") :weight bold)))
(insert (propertize "wave-underlined " 'face '(:underline (:style wave :color "red") :slant italic)))
(insert (propertize "line.\n" 'face '(:underline (:style wave :color "red"))))
(insert "\n")
;; Overline
(insert (propertize "── Overline " 'face 'shadow) (make-string 51 ?─) "\n")
(insert (propertize "Overline (default color) — The quick brown fox\n"
'face '(:overline t)))
(insert (propertize "Overline (forest green) — The quick brown fox\n"
'face '(:overline "forest green")))
(insert "\n")
;; Strike-through
(insert (propertize "── Strike-through " 'face 'shadow) (make-string 45 ?─) "\n")
(insert (propertize "Strike-through (default) — The quick brown fox\n"
'face '(:strike-through t)))
(insert (propertize "Strike-through (red) — The quick brown fox\n"
'face '(:strike-through "red")))
(insert "\n")
;; Combined
(insert (propertize "── Combined decorations " 'face 'shadow) (make-string 40 ?─) "\n")
(insert (propertize "Underline + overline — The quick brown fox\n"
'face '(:underline t :overline t)))
(insert (propertize "All three (U + O + S) — The quick brown fox\n"
'face '(:underline t :overline t :strike-through t)))
(insert "\n")
;; Colors on colored text
(insert (propertize "── Decorations on colored text " 'face 'shadow) (make-string 33 ?─) "\n")
(insert (propertize "Red text, blue underline — The quick brown fox\n"
'face '(:foreground "red" :underline (:color "cornflower blue"))))
(insert (propertize "Green bold, wave red under — The quick brown fox\n"
'face '(:foreground "forest green" :weight bold
:underline (:style wave :color "red"))))
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf))

View file

@ -0,0 +1,39 @@
#+TITLE: QA 06 — Boxes & Reliefs
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/06-boxes-reliefs/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/06-boxes-reliefs/test.el
* Checklist
** TODO 6.1 Simple box 1px — four sides visible
A 1px border must be visible on all four sides of the text span.
Failure: one or more sides missing, or the border appears inside the text.
** TODO 6.2 Box 3px with custom color — correct thickness and color
All four sides must be exactly 3px wide and in the specified color.
Failure: thickness varies between sides, or color is wrong.
** TODO 6.3 Raised relief — light top-left, dark bottom-right
Top and left edges must be lighter than the face background;
bottom and right must be darker.
Failure: edges are the wrong colors (inverted), or all the same shade.
** TODO 6.4 Sunken relief — dark top-left, light bottom-right
Top and left edges must be darker; bottom and right must be lighter.
Failure: looks raised instead of sunken.
** TODO 6.5 Relief corners — must appear dark, not light
At the corner pixels where the light and dark edges of a raised box
overlap, the resulting color must be dark (the dark edge wins).
Failure: corners appear light or have a mixed artifact.
** TODO 6.6 Relief on mode-line — consistent in light and dark themes
=(load-theme 'modus-vivendi t)= and check the mode-line relief.
Then =(load-theme 'modus-operandi t)= and check again.
Failure: relief colors become inverted or look broken in one of the themes.
** TODO 6.7 Box line widths — horizontal vs vertical can differ
Use =(:box (:line-width (2 . 4)))= to specify different H/V widths.
Failure: both widths are treated the same.

View file

@ -0,0 +1,81 @@
;;; test.el --- Visual QA: Boxes & Reliefs -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/06-boxes-reliefs/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/06-boxes-reliefs/test.el
(let ((buf (get-buffer-create "*QA: Boxes & Reliefs*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(insert (propertize "=== Boxes & Reliefs QA ===\n\n"
'face '(:weight bold :height 1.3)))
;; Simple boxes
(insert (propertize "── Simple boxes " 'face 'shadow) (make-string 48 ?─) "\n\n")
(insert " ")
(insert (propertize " Box 1px " 'face '(:box 1)))
(insert " ")
(insert (propertize " Box 2px " 'face '(:box 2)))
(insert " ")
(insert (propertize " Box 3px " 'face '(:box 3)))
(insert "\n\n")
(insert " ")
(insert (propertize " Blue box 1px " 'face '(:box (:line-width 1 :color "cornflower blue"))))
(insert " ")
(insert (propertize " Red box 2px " 'face '(:box (:line-width 2 :color "red"))))
(insert "\n\n")
;; Asymmetric line widths
(insert " ")
(insert (propertize " H=1 V=4 " 'face '(:box (:line-width (1 . 4)))))
(insert " ")
(insert (propertize " H=4 V=1 " 'face '(:box (:line-width (4 . 1)))))
(insert "\n\n")
;; Reliefs
(insert (propertize "── Reliefs " 'face 'shadow) (make-string 53 ?─) "\n\n")
(insert " ")
(insert (propertize " Raised (default) " 'face '(:box (:line-width 2 :style released-button))))
(insert " ")
(insert (propertize " Sunken (pressed) " 'face '(:box (:line-width 2 :style pressed-button))))
(insert "\n\n")
(insert " ")
(insert (propertize " Raised 3px " 'face '(:box (:line-width 3 :style released-button))))
(insert " ")
(insert (propertize " Sunken 3px " 'face '(:box (:line-width 3 :style pressed-button))))
(insert "\n\n")
;; On colored backgrounds
(insert (propertize "── Reliefs on colored backgrounds " 'face 'shadow) (make-string 30 ?─) "\n\n")
(insert " ")
(insert (propertize " Raised on blue "
'face '(:background "steel blue" :foreground "white"
:box (:line-width 2 :style released-button))))
(insert " ")
(insert (propertize " Sunken on blue "
'face '(:background "steel blue" :foreground "white"
:box (:line-width 2 :style pressed-button))))
(insert "\n\n")
;; Mode-line style simulation
(insert (propertize "── Mode-line button simulation " 'face 'shadow) (make-string 33 ?─) "\n\n")
(let ((ml-face `(:background ,(face-background 'mode-line nil t)
:foreground ,(face-foreground 'mode-line nil t)
:box (:line-width 1 :style released-button))))
(insert " ")
(insert (propertize " [Button 1] " 'face ml-face))
(insert " ")
(insert (propertize " [Button 2] " 'face ml-face))
(insert " ")
(insert (propertize " [Pressed] "
'face `(:background ,(face-background 'mode-line nil t)
:foreground ,(face-foreground 'mode-line nil t)
:box (:line-width 1 :style pressed-button)))))
(insert "\n")
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf))

53
qa/07-cursor/README.org Normal file
View file

@ -0,0 +1,53 @@
#+TITLE: QA 07 — Cursor
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/07-cursor/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/07-cursor/test.el
The test cycles through all cursor types automatically every 2 seconds.
Press =q= to stop the cycle and leave the cursor in the last mode.
* Checklist
** TODO 7.1 FILLED_BOX — inverted colors
The cursor cell must have the cursor-color background and the text
in the cursor background color (colors inverted relative to the text).
Failure: text is invisible, or cursor has no background fill.
** TODO 7.2 HOLLOW_BOX — four 1px edges, transparent inside
Four 1px lines forming a rectangle; no fill inside.
Failure: the interior is filled, or one edge is missing.
** TODO 7.3 BAR — thin vertical line, left edge of cell
A 1-2px vertical bar on the left side of the current glyph cell.
For R2L text (test the Arabic line): bar must be on the right edge.
Failure: bar is missing, too wide (>3px), or on the wrong side.
** TODO 7.4 HBAR — horizontal line, bottom of cell
A horizontal bar at the bottom of the current glyph cell.
Failure: bar is at the top, or occupies the full cell height.
** TODO 7.5 Cursor on empty line / fringe fallback
Move point to the last blank line of the buffer (after all content).
A cursor must still be visible (rendered in the fringe if no glyph).
Failure: cursor disappears completely.
** TODO 7.6 Blink off state — cursor disappears completely
With blink-cursor-mode, when the cursor blinks off, nothing must be
visible at the cursor position.
Failure: a ghost outline or partial fill remains during the off phase.
** TODO 7.7 [Metal] BLOCK — no animation on move
With gpu-cursor-animation set to block, moving point must produce
no trail, no spring motion, no effects.
Failure: cursor slides or leaves a trail.
** TODO 7.8 [Metal] SPRING — smooth damped slide
When the cursor moves a large distance, it must slide smoothly to
the target with a slight overshoot and settle in ~150ms.
Failure: cursor teleports instantly, or oscillates without settling.
** TODO 7.9 [Metal] TORPEDO — fading trail on fast movement
Moving through text quickly must leave a trail of fading cursor images.
Failure: no trail visible, or trail does not fade.

69
qa/07-cursor/test.el Normal file
View file

@ -0,0 +1,69 @@
;;; test.el --- Visual QA: Cursor -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/07-cursor/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/07-cursor/test.el
;;
;; The test cycles cursor types automatically every 2 seconds.
;; Press q to stop the cycle.
(defvar qa-cursor--types '(box hollow bar hbar)
"Cursor types to cycle through.")
(defvar qa-cursor--index 0)
(defvar qa-cursor--timer nil)
(defun qa-cursor--advance ()
(let* ((types qa-cursor--types)
(type (nth (mod qa-cursor--index (length types)) types)))
(setq cursor-type type)
(setq qa-cursor--index (1+ qa-cursor--index))
(force-mode-line-update)
(message "Cursor type: %s (press q to stop)" type)))
(defun qa-cursor-stop ()
(interactive)
(when qa-cursor--timer
(cancel-timer qa-cursor--timer)
(setq qa-cursor--timer nil))
(message "Cursor cycle stopped. Current type: %s" cursor-type))
(let ((buf (get-buffer-create "*QA: Cursor*")))
(with-current-buffer buf
(erase-buffer)
(insert (propertize "=== Cursor QA ===\n\n" 'face '(:weight bold :height 1.3)))
(insert "Cursor type cycles automatically every 2 seconds.\n")
(insert "Current type is shown in the echo area.\n")
(insert "Press q to stop the cycle.\n\n")
(insert (propertize "── Test content (move point around with arrow keys) " 'face 'shadow)
(make-string 12 ?─) "\n\n")
;; Regular ASCII for cursor positioning
(dotimes (i 8)
(insert (format " Line %d: The quick brown fox jumps over the lazy dog\n" (1+ i))))
(insert "\n")
;; R2L line for BAR cursor direction test
(insert (propertize "── R2L test for BAR cursor (bar must be on RIGHT side) " 'face 'shadow)
(make-string 7 ?─) "\n")
(insert " مرحبا بالعالم — move cursor into Arabic text and verify BAR position\n\n")
;; Empty lines for fringe cursor test
(insert (propertize "── Empty lines — cursor must be visible here too " 'face 'shadow)
(make-string 13 ?─) "\n")
(dotimes (_ 5) (insert "\n"))
(use-local-map (make-sparse-keymap))
(local-set-key "q" #'qa-cursor-stop)
(goto-char (point-min))
(forward-line 7))
(switch-to-buffer buf)
(blink-cursor-mode 1)
;; Start the cycle timer
(setq qa-cursor--index 0)
(qa-cursor--advance)
(setq qa-cursor--timer
(run-with-timer 2 2 #'qa-cursor--advance)))

39
qa/08-fringe/README.org Normal file
View file

@ -0,0 +1,39 @@
#+TITLE: QA 08 — Fringe
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/08-fringe/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/08-fringe/test.el
* Checklist
** TODO 8.1 Continuation arrows — appear when line is truncated
The right fringe must show a right-pointing arrow on truncated lines.
With hscroll active, the left fringe must show a left-pointing arrow.
Failure: fringe is blank; or arrows appear on non-truncated lines.
** TODO 8.2 Empty-line indicator — tildes at end of buffer
After the last line of content, each visual line must show a tilde
indicator in the left fringe.
Failure: no tildes, or tildes appear on content lines.
** TODO 8.3 Custom bitmap — renders at correct position and color
The custom ">>" bitmap defined in the test must appear on marked lines
in the color set by the face.
Failure: bitmap is missing, in the wrong column, or wrong color.
** TODO 8.4 Fringe background color
The entire fringe column must have the yellow background defined in
the test.
Failure: fringe background remains the default frame color.
** TODO 8.5 Tall bitmap does not bleed over the mode-line
Scroll to the last line before the mode-line. The empty-line tilde
bitmap (which can be 72px tall) must be clipped to the row's visible
area and must not overwrite the mode-line.
Failure: pixels from the bitmap appear over the mode-line.
** TODO 8.6 Fringe width changes take effect
Changing fringe-mode to a wider value must immediately make the
fringe column visibly wider.
Failure: fringe width does not change, or changes only after redisplay.

63
qa/08-fringe/test.el Normal file
View file

@ -0,0 +1,63 @@
;;; test.el --- Visual QA: Fringe -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/08-fringe/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/08-fringe/test.el
;; Custom bitmap: ">>" chevron, 8px wide, 10px tall
(define-fringe-bitmap 'qa-chevron
[#x00 #x18 #x3C #x7E #x3C #x18 #x00 #x00 #x00 #x00]
10 8 'center)
;; Face for the custom bitmap
(defface qa-fringe-mark
'((t :foreground "orange red"))
"Face for QA fringe markers.")
(let ((buf (get-buffer-create "*QA: Fringe*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
;; Yellow fringe background
(set-face-background 'fringe "lemon chiffon")
(insert (propertize "=== Fringe QA ===\n\n" 'face '(:weight bold :height 1.3)))
;; Truncation arrows: enable truncate-lines and add long lines
(insert (propertize "── Truncation arrows (truncate-lines is ON below) " 'face 'shadow)
(make-string 13 ?─) "\n")
(setq truncate-lines t)
(insert (make-string 200 ?A))
(insert "\n")
(insert (make-string 200 ?B))
(insert "\n")
(setq truncate-lines nil)
(insert "\n")
;; Custom bitmap markers
(insert (propertize "── Custom bitmap markers (orange chevron in left fringe) " 'face 'shadow)
(make-string 6 ?─) "\n")
(let ((start (point)))
(insert "Marked line 1\n")
(put-text-property start (point) 'display
`(left-fringe qa-chevron qa-fringe-mark)))
(let ((start (point)))
(insert "Marked line 2\n")
(put-text-property start (point) 'display
`(left-fringe qa-chevron qa-fringe-mark)))
(insert "Unmarked line\n")
(let ((start (point)))
(insert "Marked line 3\n")
(put-text-property start (point) 'display
`(left-fringe qa-chevron qa-fringe-mark)))
(insert "\n")
;; Empty-line indicators: go past the content
(insert (propertize "── Empty-line indicators (tildes after last line) " 'face 'shadow)
(make-string 13 ?─) "\n")
(setq indicate-empty-lines t)
(insert "Last content line — scroll down to see tildes below.\n")
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf))

50
qa/09-images/README.org Normal file
View file

@ -0,0 +1,50 @@
#+TITLE: QA 09 — Images
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/09-images/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/09-images/test.el
The test uses Emacs built-in images from =data-directory=.
For JPEG and external SVG tests, provide paths to your own files when prompted.
* Checklist
** TODO 9.1 PNG opaque — correct colors, sharp edges
The built-in PNG icons must appear with correct colors and sharp edges.
Failure: washed out colors, blurry edges, or image not displayed.
** TODO 9.2 PNG with alpha channel — background shows through
The icons with transparency must composite correctly against the
Emacs background; the fringe/frame background must be visible through
transparent areas.
Failure: transparent areas appear as white or black rectangles.
** TODO 9.3 SVG — sharp at any scale
The built-in SVG must appear crisp with no pixelation.
Failure: blocky rendering, or SVG shown as a broken-image placeholder.
** TODO 9.4 Image with :scale 2.0 — visibly larger
The scaled version must be exactly twice the size of the original.
Failure: both images appear the same size.
** TODO 9.5 Image with :rotation 90 — correctly rotated
The rotated image must appear turned 90° clockwise.
Failure: image appears unrotated, or is blank.
** TODO 9.6 Image slice — only specified portion visible
Only the top-left 32×32 pixels of the source image must be shown.
Failure: the full image is shown, or the wrong quadrant.
** TODO 9.7 Image with :relief — raised border around it
A raised-relief border must be visible around the image.
Failure: no border, or border has wrong colors (sunken instead of raised).
** TODO 9.8 Image cache — two insertions identical
The same image inserted twice must look identical in both places.
Failure: second insertion looks different (wrong colors, size, or missing).
** TODO 9.9 Image adjacent to text — correct vertical alignment
Images must align with the text baseline; they must not shift the
surrounding line height excessively.
Failure: large gap above or below the image, or text and image overlap.

84
qa/09-images/test.el Normal file
View file

@ -0,0 +1,84 @@
;;; test.el --- Visual QA: Images -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/09-images/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/09-images/test.el
(defun qa-find-builtin-image (names)
"Return path to first existing built-in image from NAMES list."
(let ((dirs (list (expand-file-name "images/" data-directory)
(expand-file-name "images/icons/" data-directory)
data-directory)))
(catch 'found
(dolist (name names)
(dolist (dir dirs)
(let ((path (expand-file-name name dir)))
(when (file-exists-p path)
(throw 'found path))))))))
(let* ((buf (get-buffer-create "*QA: Images*"))
;; Locate built-in images
(png-path (qa-find-builtin-image '("icons/hicolor/32x32/apps/emacs.png"
"splash.png" "gnu-emacs-logo.png")))
(svg-path (qa-find-builtin-image '("icons/hicolor/scalable/apps/emacs.svg"
"splash.svg")))
(xpm-path (qa-find-builtin-image '("splash.xpm"))))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(insert (propertize "=== Images QA ===\n\n" 'face '(:weight bold :height 1.3)))
;; PNG
(insert (propertize "── PNG (opaque, built-in) " 'face 'shadow) (make-string 38 ?─) "\n")
(if png-path
(progn
(insert "Normal: ")
(insert-image (create-image png-path 'png nil :scale 1.0))
(insert " Next to text on the same line\n\n")
(insert "Scale 2×: ")
(insert-image (create-image png-path 'png nil :scale 2.0))
(insert "\n\n")
(insert "Rotation 90°: ")
(insert-image (create-image png-path 'png nil :rotation 90))
(insert "\n\n")
(insert "Slice (top-left 32×32): ")
(insert-image (create-image png-path 'png nil) nil nil '(0 0 32 32))
(insert "\n\n")
(insert "With :relief 3: ")
(insert-image (create-image png-path 'png nil :relief 3))
(insert "\n\n")
(insert "Same image twice (cache test): ")
(insert-image (create-image png-path 'png nil :scale 1.0))
(insert " ")
(insert-image (create-image png-path 'png nil :scale 1.0))
(insert "\n\n"))
(insert "(PNG built-in image not found — add path manually)\n\n"))
;; SVG
(insert (propertize "── SVG (built-in, vector) " 'face 'shadow) (make-string 38 ?─) "\n")
(if (and svg-path (image-type-available-p 'svg))
(progn
(insert "SVG at 1×: ")
(insert-image (create-image svg-path 'svg nil :scale 0.3))
(insert " SVG at 2×: ")
(insert-image (create-image svg-path 'svg nil :scale 0.6))
(insert "\n\n"))
(insert "(SVG not available — install librsvg or provide path)\n\n"))
;; Adjacent to text (vertical alignment)
(insert (propertize "── Images adjacent to text (alignment check) " 'face 'shadow)
(make-string 18 ?─) "\n")
(when png-path
(insert "Text before ")
(insert-image (create-image png-path 'png nil :scale 0.5))
(insert " text after — baseline must be aligned\n"))
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf))

49
qa/10-scroll/README.org Normal file
View file

@ -0,0 +1,49 @@
#+TITLE: QA 10 — Scroll & copy_region
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/10-scroll/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/10-scroll/test.el
Press =s= to start the auto-scroll demo, =S= to stop.
* Checklist
** TODO 10.1 Scroll down — no artifacts on new content
Press =C-v= or =C-n= repeatedly. The text that slides up must not leave
ghost pixels. New content at the bottom must appear cleanly.
Failure: horizontal bands of garbage at the scroll boundary.
** TODO 10.2 Scroll up — same as above but reversed
Press =M-v= or =C-p=. New content at the top must appear without artifacts.
Failure: garbage lines appear at the top when new content is drawn.
** TODO 10.3 Auto-scroll — smooth and continuous
Press =s= to start the timer-based auto-scroll (1 line per 60ms).
The text must slide smoothly without jumps or blank frames.
Failure: text jumps 23 lines at a time, or a blank frame flashes.
** TODO 10.4 Horizontal scroll — hscroll, C-e to a long line
Press =C-e= on the long-line section to hscroll to the end.
The content must slide horizontally without artifacts.
Failure: garbled characters or wrong content visible after hscroll.
** TODO 10.5 [Metal] Scroll easing — EASE_OUT_QUAD
Run with: =(setq gpu-scroll-easing 'ease-out-quad)= then scroll.
The scroll must decelerate smoothly near the target.
Failure: scroll is instant (easing not applied) or overshoots.
** TODO 10.6 [Metal] Scroll easing — SPRING
Run with: =(setq gpu-scroll-easing 'spring)= then scroll.
The scroll must overshoot slightly and bounce back.
Failure: no overshoot visible, or motion never settles.
** TODO 10.7 Overlapping copy_region — 1-line scroll
Scrolling by exactly one line means src and dst regions overlap.
Content must move correctly (scratch-texture bounce path used internally).
Failure: the overlapping region shows corrupted or wrong content.
** TODO 10.8 Shift on insert — glyph shift correct
Go to the middle of a content line and insert a character.
Text to the right must shift exactly one character width.
Failure: text after the insertion point overlaps, or a gap appears.

67
qa/10-scroll/test.el Normal file
View file

@ -0,0 +1,67 @@
;;; test.el --- Visual QA: Scroll & copy_region -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/10-scroll/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/10-scroll/test.el
;;
;; Press s to start auto-scroll, S to stop.
(defvar qa-scroll--timer nil)
(defun qa-scroll-start ()
(interactive)
(setq qa-scroll--timer
(run-with-timer 0 0.06
(lambda ()
(with-current-buffer "*QA: Scroll*"
(condition-case nil
(scroll-up 1)
(end-of-buffer
(goto-char (point-min)))))))))
(defun qa-scroll-stop ()
(interactive)
(when qa-scroll--timer
(cancel-timer qa-scroll--timer)
(setq qa-scroll--timer nil)))
(let ((buf (get-buffer-create "*QA: Scroll*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(insert (propertize "=== Scroll & copy_region QA ===\n" 'face '(:weight bold :height 1.3)))
(insert "s = start auto-scroll S = stop\n")
(insert (make-string 65 ?─) "\n\n")
;; Numbered content lines for scroll verification
(dotimes (i 200)
(insert (format "%4d │ " (1+ i)))
;; Vary content to make artifact detection easy
(cond
((zerop (mod i 5))
(insert (propertize (format "CHECKPOINT %d — bold marker line" (/ i 5))
'face '(:weight bold :foreground "cornflower blue"))))
((zerop (mod i 3))
(insert (propertize "The quick brown fox jumps over the lazy dog"
'face '(:slant italic))))
(t
(insert "The quick brown fox jumps over the lazy dog 1234567890")))
(insert "\n"))
(insert "\n")
(insert (propertize "── Long lines for hscroll test " 'face 'shadow)
(make-string 33 ?─) "\n")
(setq truncate-lines t)
(dotimes (i 5)
(insert (format "HSCROLL-%d: " (1+ i)))
(insert (make-string 200 (+ ?a i)))
(insert "\n"))
(setq truncate-lines nil)
(use-local-map (make-sparse-keymap))
(local-set-key "s" #'qa-scroll-start)
(local-set-key "S" #'qa-scroll-stop)
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf))

48
qa/11-bars/README.org Normal file
View file

@ -0,0 +1,48 @@
#+TITLE: QA 11 — Emacs Bars
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/11-bars/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/11-bars/test.el
* Checklist
** TODO 11.1 Mode-line — content and colors correct
The mode-line must show buffer name, major mode, and position.
Background/foreground must match the mode-line face.
Failure: mode-line is blank, wrong colors, or overlapping text.
** TODO 11.2 Mode-line — inactive window uses mode-line-inactive
Click on the mode-line area of a different split (after C-x 2).
The active window's mode-line must be brighter than the inactive one.
Failure: both mode-lines have identical appearance.
** TODO 11.3 Mode-line — mouse-face highlight on buttons
Move the mouse over the major-mode name or buffer-name area.
A highlight must appear while the mouse is over the button.
Failure: no mouse-face change, or highlight flickers off immediately.
** TODO 11.4 Header-line — visible above text area
The header-line set in the test must appear between the tab-bar and
the text area.
Failure: header-line is missing or overlaps the first text line.
** TODO 11.5 Tab-bar — tabs switch buffers cleanly
Click each tab in the tab-bar. The content area must update without
any flash or residual content from the previous tab.
Failure: flash of old content, or a blank frame between tabs.
** TODO 11.6 Tab-bar — hover highlight on tabs
Hover the mouse over individual tabs. Each tab must highlight on hover.
Failure: no hover effect, or highlight persists after mouse leaves.
** TODO 11.7 Tool-bar — icons visible with raised relief
The tool-bar must show icons with correct relief (raised = clickable).
Failure: icons are missing, icons appear flat (no relief), or
tool-bar area is blank.
** TODO 11.8 Scroll-bar — tracks buffer position
Drag the scroll-bar thumb. The buffer must scroll to the corresponding
position.
Failure: thumb position does not reflect the buffer position, or
the scroll-bar background is rendered incorrectly.

50
qa/11-bars/test.el Normal file
View file

@ -0,0 +1,50 @@
;;; test.el --- Visual QA: Emacs Bars -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/11-bars/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/11-bars/test.el
;; Enable all bars
(tool-bar-mode 1)
(scroll-bar-mode 1)
(tab-bar-mode 1)
;; Open a few tabs to make the tab-bar useful
(tab-new)
(tab-rename "Tab-A")
(tab-new)
(tab-rename "Tab-B")
(tab-new)
(tab-rename "QA")
(tab-bar-select-tab 3)
(let ((buf (get-buffer-create "*QA: Bars*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
;; Header-line
(setq header-line-format
(propertize " Header-line: verify it is above this text "
'face '(:background "steel blue" :foreground "white")))
(insert (propertize "=== Emacs Bars QA ===\n\n" 'face '(:weight bold :height 1.3)))
(insert "Bars enabled: tool-bar, scroll-bar, tab-bar, header-line.\n\n")
(insert (propertize "Checklist:\n" 'face '(:weight bold)))
(insert " 1. Mode-line shows buffer name + major mode + position\n")
(insert " 2. C-x 2 to split — active mode-line brighter than inactive\n")
(insert " 3. Hover mouse over mode-line buttons — highlight must appear\n")
(insert " 4. Header-line visible in blue above this text\n")
(insert " 5. Tab-bar shows Tab-A, Tab-B, QA — click to switch\n")
(insert " 6. Hover over each tab — highlight on hover\n")
(insert " 7. Tool-bar shows icons with raised relief\n")
(insert " 8. Drag scroll-bar thumb — buffer scrolls to matching position\n\n")
(insert (make-string 65 ?─) "\n")
(insert "Padding content for the scroll-bar to be meaningful:\n\n")
(dotimes (i 80)
(insert (format " Line %3d: The quick brown fox jumps over the lazy dog\n" (1+ i))))
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf)))

42
qa/12-windows/README.org Normal file
View file

@ -0,0 +1,42 @@
#+TITLE: QA 12 — Windows & Splits
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/12-windows/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/12-windows/test.el
* Checklist
** TODO 12.1 Vertical split — divider visible, no content overlap
After =C-x 2=, the horizontal dividing line must be clearly visible.
Both window content areas must not overlap.
Failure: no dividing line visible, or the mode-line of the upper
window overlaps the first text line of the lower window.
** TODO 12.2 Horizontal split — vertical divider visible
After =C-x 3=, a vertical dividing line must separate the windows.
Failure: no line visible between windows, or content is shared.
** TODO 12.3 Window divider >= 3px — first/last pixel colors distinct
The test sets window-divider width to 6px. The leftmost and rightmost
pixels of the divider must have different colors.
Failure: the divider appears as a single solid color stripe.
** TODO 12.4 Resize windows — no artifacts
Resize with =C-x ^= (taller) and =C-x {= / =C-x }= (narrower/wider).
Failure: garbled content or black/white bands appear after resize.
** TODO 12.5 Internal border — correct color and width
The frame must show a 6px border around all its edges in the color
defined by the internal-border face.
Failure: border is missing, wrong width, or wrong color.
** TODO 12.6 Multiple frames — independent rendering
Press =n= to open a second frame. Both frames must render their own
content independently.
Failure: one frame shows the content of the other, or one frame goes blank.
** TODO 12.7 Child frame — correct position and no border artifacts
Press =p= to open a child frame popup. It must appear centered over
the parent with clean edges (no black or white border bleeding).
Failure: the popup has a visible artifact on one or more edges.

70
qa/12-windows/test.el Normal file
View file

@ -0,0 +1,70 @@
;;; test.el --- Visual QA: Windows & Splits -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/12-windows/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/12-windows/test.el
;;
;; Keys: 2 = vertical split, 3 = horizontal split, n = new frame, p = popup child frame
(setq window-divider-default-bottom-width 6
window-divider-default-right-width 6
window-divider-default-places t)
(window-divider-mode 1)
(set-frame-parameter nil 'internal-border-width 6)
(defun qa-windows-new-frame ()
(interactive)
(make-frame '((name . "QA Second Frame")
(width . 60) (height . 30))))
(defun qa-windows-child-popup ()
(interactive)
(let* ((parent (selected-frame))
(pw (frame-pixel-width parent))
(ph (frame-pixel-height parent))
(fw 400) (fh 200)
(child (make-frame
`((parent-frame . ,parent)
(left . ,(/ (- pw fw) 2))
(top . ,(/ (- ph fh) 2))
(width . 50) (height . 8)
(undecorated . t)
(background-color . "lightyellow")
(name . "QA Popup")))))
(with-selected-frame child
(switch-to-buffer (get-buffer-create "*QA Popup*"))
(erase-buffer)
(insert "\n This is a child frame popup.\n")
(insert " Verify: clean edges, correct position.\n")
(insert " Press q to close.\n")
(local-set-key "q" (lambda () (interactive) (delete-frame))))))
(let ((buf (get-buffer-create "*QA: Windows*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(insert (propertize "=== Windows & Splits QA ===\n\n" 'face '(:weight bold :height 1.3)))
(insert (propertize "Keys:\n" 'face '(:weight bold)))
(insert " C-x 2 — vertical split\n")
(insert " C-x 3 — horizontal split\n")
(insert " C-x 1 — back to single window\n")
(insert " n — open a second frame\n")
(insert " p — open a child-frame popup\n\n")
(insert (propertize "Window divider: 6px (should show first/last pixel colors)\n"
'face '(:foreground "cornflower blue")))
(insert (propertize "Internal border: 6px around the frame edges\n\n"
'face '(:foreground "cornflower blue")))
(insert (make-string 65 ?─) "\n")
(dotimes (i 40)
(insert (format " Line %3d: content for window resize and split testing\n" (1+ i))))
(use-local-map (make-sparse-keymap))
(local-set-key "n" #'qa-windows-new-frame)
(local-set-key "p" #'qa-windows-child-popup)
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf))

View file

@ -0,0 +1,54 @@
#+TITLE: QA 13 — Display Properties & Overlays
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/13-display-props/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/13-display-props/test.el
* Checklist
** TODO 13.1 Invisible text — not visible, cursor skips over it
The hidden word in the first section must not appear on screen.
Moving point through it must skip to the next visible character.
Failure: invisible characters are shown, or cursor stops on them.
** TODO 13.2 Display replacement string — replaces original text
The word "HIDDEN" must appear where the original text was, not the
original word.
Failure: both texts shown, original text shown, or the area is blank.
** TODO 13.3 Overlay face — yellow background on marked region
The overlay region must have a yellow background.
Failure: background not applied, or applied to wrong region.
** TODO 13.4 Overlay after-string / before-string — correct position
The "«before»" and "«after»" strings must appear immediately adjacent
to their anchor point.
Failure: extra spacing, wrong position, or strings not shown.
** TODO 13.5 Line numbers — right-aligned in margin
Line numbers must be right-aligned in a fixed-width left margin.
The numbers must update correctly after scrolling.
Failure: numbers are left-aligned, wrong, or overlap the text area.
** TODO 13.6 hl-line — highlight follows cursor line
The current line must have a distinct background highlight.
The highlight must move when the cursor moves to another line.
Failure: highlight is on the wrong line, or does not update.
** TODO 13.7 Left margin — blank column left of text
A 4-column blank margin must appear to the left of the text.
Failure: margin is missing, wrong width, or shows garbage.
** TODO 13.8 Display space — explicit column alignment
The aligned column in the "Columns" section must line up correctly
regardless of the font's variable pitch.
Failure: the content after the space is misaligned.
** TODO 13.9 Wrap / visual-line-mode — wrap at word boundary
Long lines must wrap at word boundaries.
Failure: wrap occurs mid-word, or lines do not wrap at all.
** TODO 13.10 Truncate-lines — fringe arrow visible
Long lines must be cut with a right-fringe truncation arrow.
Failure: lines wrap anyway, or no fringe arrow is shown.

View file

@ -0,0 +1,80 @@
;;; test.el --- Visual QA: Display Properties & Overlays -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/13-display-props/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/13-display-props/test.el
(let ((buf (get-buffer-create "*QA: Display Props*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(display-line-numbers-mode 1)
(hl-line-mode 1)
(setq left-margin-width 4)
(set-window-margins (get-buffer-window buf) 4 0)
(insert (propertize "=== Display Properties & Overlays QA ===\n\n"
'face '(:weight bold :height 1.3)))
;; Invisible text
(insert (propertize "── Invisible text " 'face 'shadow) (make-string 46 ?─) "\n")
(insert "Visible ")
(let ((start (point)))
(insert "INVISIBLE")
(put-text-property start (point) 'invisible t))
(insert " visible again — the word INVISIBLE must not appear.\n\n")
;; Display replacement
(insert (propertize "── Display string replacement " 'face 'shadow) (make-string 34 ?─) "\n")
(insert "This word: ")
(let ((start (point)))
(insert "ORIGINAL")
(put-text-property start (point) 'display "REPLACED"))
(insert " — must read 'REPLACED', not 'ORIGINAL'.\n\n")
;; Display space
(insert (propertize "── Display space (:width alignment) " 'face 'shadow) (make-string 27 ?─) "\n")
(insert "Col 1")
(insert (propertize " " 'display '(space :align-to 20)))
(insert "Col 2 (aligned to column 20)\n")
(insert "Short")
(insert (propertize " " 'display '(space :align-to 20)))
(insert "Also at column 20\n\n")
;; Visual-line-mode wrapping
(insert (propertize "── Visual-line-mode wrapping " 'face 'shadow) (make-string 35 ?─) "\n")
(visual-line-mode 1)
(insert "This is a very long line that should wrap at word boundaries and not in the middle of any word, demonstrating proper word-wrap behavior in the GPU backend.\n")
(visual-line-mode -1)
(insert "\n")
;; Truncate-lines
(insert (propertize "── Truncate-lines (fringe arrow must appear) " 'face 'shadow)
(make-string 18 ?─) "\n")
(setq truncate-lines t)
(insert "Truncated: ")
(insert (make-string 180 ?X))
(insert "\n")
(setq truncate-lines nil)
(insert "\n")
;; Overlays (applied after building buffer content)
(let ((ov-start nil))
(insert (propertize "── Overlay face + before/after strings " 'face 'shadow)
(make-string 24 ?─) "\n")
(setq ov-start (point))
(insert "This region has an overlay with a yellow background.")
(let ((ov (make-overlay ov-start (point))))
(overlay-put ov 'face '(:background "lemon chiffon")))
(insert "\n"))
(let ((anchor (point)))
(insert "before-string«anchor»after-string on this line.\n")
(let ((ov (make-overlay anchor (1+ anchor))))
(overlay-put ov 'before-string (propertize "«BEFORE» " 'face '(:foreground "red")))
(overlay-put ov 'after-string (propertize " «AFTER»" 'face '(:foreground "blue")))))
(insert "\n")
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf))

View file

@ -0,0 +1,43 @@
#+TITLE: QA 14 — Major Modes (Real-World Integration)
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/14-major-modes/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/14-major-modes/test.el
This test requires packages (org, dired) which are built into Emacs.
magit and corfu, if installed, are tested separately — see notes.
* Checklist
** TODO 14.1 org-mode — headings, code blocks, tables
The org buffer must show:
- Headings at different levels with distinct colors
- A code block with a shaded background
- A table with correct alignment
Failure: headings are plain text, code block background missing, or
table columns are misaligned.
** TODO 14.2 org-mode — emphasis markup
Bold, italic, code, and verbatim spans must each render with their
respective face (not as literal asterisks).
Failure: markup characters visible, or all emphasis looks the same.
** TODO 14.3 emacs-lisp-mode — syntax highlight in code buffer
The Emacs Lisp buffer must show keyword/builtin/string/comment colors.
Failure: all text appears in the default face.
** TODO 14.4 dired — file listing with correct alignment
Open dired on the home directory. Permissions, size, date, and filename
columns must be aligned correctly.
Failure: columns are misaligned, or colored file types are wrong.
** TODO 14.5 dired — face colors for file types
Directories must appear in the directory face (typically blue/bold).
Executable files must appear in a different face.
Failure: all files appear in the same face.
** TODO 14.6 Large org file — no lag on first display
Opening the synthetic 5k-line org file must display the first page
within 1 second without visible jank.
Failure: several-second freeze before the buffer appears.

88
qa/14-major-modes/test.el Normal file
View file

@ -0,0 +1,88 @@
;;; test.el --- Visual QA: Major Modes -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/14-major-modes/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/14-major-modes/test.el
;;
;; Press d to open dired on $HOME, o to open the org buffer, e for Emacs Lisp.
(defun qa-modes-open-org ()
(interactive)
(let ((buf (get-buffer-create "*QA: org-mode*")))
(with-current-buffer buf
(erase-buffer)
(insert "* Heading Level 1\n\n")
(insert "Regular paragraph text with *bold*, /italic/, =code=,\n")
(insert "and ~verbatim~ spans. Also _underlined_ text.\n\n")
(insert "** Heading Level 2\n\n")
(insert "A table:\n\n")
(insert "| Name | Value | Status |\n")
(insert "|------------+-------+---------|\n")
(insert "| Alpha | 100 | active |\n")
(insert "| Beta | 200 | pending |\n")
(insert "| Gamma | 300 | done |\n\n")
(insert "*** Heading Level 3\n\n")
(insert "A source block:\n\n")
(insert "#+begin_src emacs-lisp\n")
(insert "(defun greet (name)\n")
(insert " (message \"Hello, %s!\" name))\n")
(insert "#+end_src\n\n")
(insert "**** Heading Level 4\n\n")
(insert "- List item one\n")
(insert "- List item two\n")
(insert " - Nested item\n")
(insert "- List item three\n\n")
(insert "***** Heading Level 5\n\n")
(insert "A longer paragraph to check line wrapping and text rendering\n")
(insert "at normal reading width. The GPU backend must render this\n")
(insert "identically to the NS or cairo baseline.\n")
(org-mode))
(switch-to-buffer buf)))
(defun qa-modes-open-elisp ()
(interactive)
(let ((buf (get-buffer-create "*QA: emacs-lisp-mode*")))
(with-current-buffer buf
(erase-buffer)
(insert ";; Emacs Lisp syntax highlight test\n\n")
(insert "(require 'cl-lib)\n\n")
(insert "(defvar qa-counter 0\n")
(insert " \"A simple counter variable.\")\n\n")
(insert "(defun qa-increment (&optional amount)\n")
(insert " \"Increment QA-COUNTER by AMOUNT (default 1).\"\n")
(insert " (interactive)\n")
(insert " (cl-incf qa-counter (or amount 1))\n")
(insert " (message \"Counter: %d\" qa-counter))\n\n")
(insert "(defmacro qa-with-timer (name &rest body)\n")
(insert " \"Run BODY and report elapsed time under NAME.\"\n")
(insert " `(let ((start (float-time)))\n")
(insert " ,@body\n")
(insert " (message \"%s took %.3fs\" ',name (- (float-time) start))))\n\n")
(insert ";; String literals and constants\n")
(insert "(defconst qa-version \"1.0.0\" \"QA harness version.\")\n")
(insert "(defconst qa-max-lines 1000)\n\n")
(insert ";; Keyword arguments\n")
(insert "(cl-defun qa-make-item (&key name value (enabled t))\n")
(insert " (list :name name :value value :enabled enabled))\n")
(emacs-lisp-mode)
(font-lock-ensure))
(switch-to-buffer buf)))
(defun qa-modes-open-dired ()
(interactive)
(dired "~"))
(let ((buf (get-buffer-create "*QA: Major Modes*")))
(with-current-buffer buf
(erase-buffer)
(insert (propertize "=== Major Modes QA ===\n\n" 'face '(:weight bold :height 1.3)))
(insert (propertize "Keys:\n" 'face '(:weight bold)))
(insert " o — open org-mode buffer\n")
(insert " e — open emacs-lisp-mode buffer\n")
(insert " d — open dired on $HOME\n\n")
(insert "For each mode, compare with the vanilla baseline side by side.\n")
(use-local-map (make-sparse-keymap))
(local-set-key "o" #'qa-modes-open-org)
(local-set-key "e" #'qa-modes-open-elisp)
(local-set-key "d" #'qa-modes-open-dired))
(switch-to-buffer buf))

View file

@ -0,0 +1,51 @@
#+TITLE: QA 15 — Buffer Transitions & HiDPI
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/15-transitions/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/15-transitions/test.el
HiDPI tests are macOS-only (Retina display required).
* Checklist — Buffer transitions
** TODO 15.1 Crossfade — previous buffer fades out
Press =t= to cycle between buffers with crossfade enabled.
The previous buffer content must fade out smoothly as the new one appears.
Failure: hard cut (no fade), or the fade shows garbled intermediate frames.
** TODO 15.2 Crossfade duration — matches configured value
=(setq gpu-buffer-transition-duration 0.5)= should produce a 0.5s fade.
Failure: fade is instantaneous or takes much longer than configured.
** TODO 15.3 No residue after fade completes
After the crossfade finishes, the new buffer must appear perfectly clean.
Failure: faint traces of the previous buffer remain after the fade ends.
** TODO 15.4 Pump cadence during fade — 60Hz, smooth
The fade must be smooth (not choppy). At 0.5s duration it should
produce ~30 intermediate frames.
Failure: visible frame-rate drops during the fade; choppy animation.
** TODO 15.5 No crossfade when disabled
=(setq gpu-buffer-transitions nil)=
Pressing =t= must produce an instant hard cut with no fade.
Failure: fade still occurs after disabling.
* Checklist — HiDPI (macOS Retina only)
** TODO 15.6 [macOS] Capture size is 2× the logical frame size
=(gpu-capture-frame "/tmp/qa-capture.png")=
Open the PNG and verify its pixel dimensions are exactly 2×
the Emacs frame's logical pixel width and height.
Failure: capture is 1× size (not Retina-aware).
** TODO 15.7 [macOS] Glyph sharpness — per-physical-pixel AA
Zoom into the capture at 6× (pixel viewer or Preview zoom).
Each glyph must show antialiasing on individual physical pixels.
Failure: glyphs appear as upscaled 1× bitmaps (blocky edges).
** TODO 15.8 [macOS] NS vs Metal parity at 2×
Compare a Metal capture with a NS capture (EMACS_GPU_DISABLE=1).
Acceptable: fuzz5% ~0.5%, fuzz20% ~0.05% (AA on glyph edges only).
Failure: large structural differences (wrong colors, misaligned text).

59
qa/15-transitions/test.el Normal file
View file

@ -0,0 +1,59 @@
;;; test.el --- Visual QA: Buffer Transitions & HiDPI -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/15-transitions/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/15-transitions/test.el
;;
;; Press t to cycle buffers with crossfade, T to toggle crossfade on/off.
(defvar qa-trans--buffers nil)
(defvar qa-trans--index 0)
(defun qa-trans-cycle ()
(interactive)
(setq qa-trans--index (mod (1+ qa-trans--index) (length qa-trans--buffers)))
(switch-to-buffer (nth qa-trans--index qa-trans--buffers)))
(defun qa-trans-toggle ()
(interactive)
(if (boundp 'gpu-buffer-transitions)
(progn
(setq gpu-buffer-transitions (not gpu-buffer-transitions))
(message "gpu-buffer-transitions: %s" gpu-buffer-transitions))
(message "gpu-buffer-transitions not available (vanilla mode)")))
;; Build a set of visually distinct buffers to fade between
(let ((colors '(("cornflower blue" . "white")
("forest green" . "white")
("firebrick" . "white")
("dark orange" . "black")))
bufs)
(dolist (pair colors)
(let* ((bg (car pair)) (fg (cdr pair))
(name (format "*QA: Transition %s*" bg))
(buf (get-buffer-create name)))
(with-current-buffer buf
(erase-buffer)
(face-remap-add-relative 'default `(:background ,bg :foreground ,fg))
(insert (format "\n\n Buffer: %s\n\n" name))
(insert " This buffer has a distinct background color.\n")
(insert " When crossfade is ON (press t), the previous buffer\n")
(insert " must fade out smoothly as this one fades in.\n\n")
(insert " Press t to cycle to the next buffer.\n")
(insert " Press T to toggle crossfade on/off.\n\n")
(dotimes (i 20)
(insert (format " Line %d: padding content\n" (1+ i))))
(use-local-map (make-sparse-keymap))
(local-set-key "t" #'qa-trans-cycle)
(local-set-key "T" #'qa-trans-toggle))
(push buf bufs)))
(setq qa-trans--buffers (nreverse bufs))
(setq qa-trans--index 0)
;; Enable transitions if available
(when (boundp 'gpu-buffer-transitions)
(setq gpu-buffer-transitions t)
(setq gpu-buffer-transition-duration 0.4))
(switch-to-buffer (car qa-trans--buffers))
(message "Crossfade QA ready. t = cycle buffers, T = toggle crossfade"))

51
qa/16-video/README.org Normal file
View file

@ -0,0 +1,51 @@
#+TITLE: QA 16 — Inline Video (macOS Metal only)
#+TODO: TODO | PASS FAIL SKIP
* Platform note
This test applies only to macOS with the Metal backend.
Mark all items as SKIP on Linux / GL.
* How to run
: emacs --no-init-file -l qa/16-video/test.el
: (provide a .mp4 path when prompted)
Vanilla baseline: N/A (video is a GPU-only feature).
* Checklist
** TODO 16.1 SKIP [Linux/GL] Placeholder appears after gpu-video-insert
After inserting a video, a blank placeholder (space) must appear
at the insertion point.
Failure: nothing is inserted, or an error is thrown.
** TODO 16.2 SKIP [Linux/GL] Video plays at the placeholder position
When playback starts, the video must appear over the placeholder,
not at a fixed screen position.
Failure: video appears at the top-left of the frame, or not at all.
** TODO 16.3 SKIP [Linux/GL] Video follows scroll
Scroll the buffer while the video is playing. The video must move
with the text.
Failure: video stays fixed at its initial screen position while the
text scrolls underneath.
** TODO 16.4 SKIP [Linux/GL] Video clipped to window interior
The video must not bleed over the mode-line or over other windows.
Failure: video pixels are visible over the mode-line or in an
adjacent split window.
** TODO 16.5 SKIP [Linux/GL] gpu-video-stop removes playback
After stopping, the placeholder must remain but the video overlay
must disappear.
Failure: video frame remains frozen on screen after stop.
** TODO 16.6 SKIP [Linux/GL] Cadence — stable 30fps
During playback, the frame rate must be stable (~33ms per frame).
Check with: =(setq gpu-video-debug t)= and observe the echo area.
Failure: visible stutter (frame drops), or echo area shows >50ms gaps.
** TODO 16.7 SKIP [Linux/GL] No interference with text rendering
During video playback, typing text or scrolling must not corrupt
the video overlay or the text in the buffer.
Failure: video pixels bleed into the text area, or text rendering
is corrupted while the video is playing.

57
qa/16-video/test.el Normal file
View file

@ -0,0 +1,57 @@
;;; test.el --- Visual QA: Inline Video (macOS Metal only) -*- lexical-binding: t; -*-
;;
;; GPU Metal: emacs --no-init-file -l qa/16-video/test.el
;; Linux/GL: All items in this test are SKIP.
;;
;; Press v to insert a video file, s to stop playback.
(defun qa-video-insert ()
(interactive)
(if (not (fboundp 'gpu-video-insert))
(message "gpu-video-insert not available (not Metal, or GPU disabled)")
(let ((path (read-file-name "Video file (.mp4/.mov): " "~/" nil t)))
(when (file-exists-p path)
(gpu-video-insert path)
(message "Video inserted. Scroll the buffer to test tracking.")))))
(defun qa-video-stop ()
(interactive)
(if (fboundp 'gpu-video-stop)
(progn (gpu-video-stop) (message "Video stopped."))
(message "gpu-video-stop not available")))
(let ((buf (get-buffer-create "*QA: Video*")))
(with-current-buffer buf
(erase-buffer)
(insert (propertize "=== Inline Video QA (macOS Metal only) ===\n\n"
'face '(:weight bold :height 1.3)))
(if (not (fboundp 'gpu-video-insert))
(insert (propertize "gpu-video-insert is not available.\nAll items in this test are SKIP.\n"
'face '(:foreground "orange red")))
(insert (propertize "Keys:\n" 'face '(:weight bold)))
(insert " v — insert a video file at point\n")
(insert " s — stop the current video\n\n")
(insert "Steps:\n")
(insert " 1. Press v and select a .mp4 or .mov file\n")
(insert " 2. Verify the video appears at the insertion point below\n")
(insert " 3. Scroll down — video must follow the text\n")
(insert " 4. Open a split (C-x 2) — video must not bleed into the other window\n")
(insert " 5. Press s — video overlay must disappear, placeholder stays\n\n"))
(insert (make-string 65 ?─) "\n")
(insert "[ video will be inserted below this line ]\n\n")
(insert (make-string 65 ?─) "\n\n")
(dotimes (i 40)
(insert (format " Line %3d: padding so you can scroll to test video tracking\n" (1+ i))))
(use-local-map (make-sparse-keymap))
(local-set-key "v" #'qa-video-insert)
(local-set-key "s" #'qa-video-stop)
;; Position point at the insertion line
(goto-char (point-min))
(search-forward "[ video will be inserted" nil t)
(forward-line 1))
(switch-to-buffer buf))

View file

@ -0,0 +1,43 @@
#+TITLE: QA 17 — Edge Cases & Performance
#+TODO: TODO | PASS FAIL SKIP
* How to run
: emacs --no-init-file -l qa/17-edge-cases/test.el
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/17-edge-cases/test.el
* Checklist
** TODO 17.1 Large buffer — no lag on scroll
Press =l= to load the 5000-line buffer. Scrolling with =C-v= / =M-v=
must feel instant (no multi-second freezes).
Failure: visible lag > 0.5s per scroll step.
** TODO 17.2 Large buffer — correct rendering throughout
Scroll through the entire large buffer. Every line must render correctly;
no lines may appear garbled or blank mid-buffer.
Failure: garbled lines appear after extended scrolling.
** TODO 17.3 Glyph atlas stress — many fonts and sizes
Press =f= to open the font stress test (many different faces/sizes).
All glyphs must render correctly even after the atlas may have reset.
Failure: some glyphs appear as blank squares after the atlas overflows.
** TODO 17.4 Theme switch — no residual artifacts
Press =m= for modus-vivendi (dark) then =o= for modus-operandi (light).
All colors must update immediately with no artifacts from the previous theme.
Failure: old-theme colors remain in parts of the buffer.
** TODO 17.5 Iconify and restore — full redraw
Minimize the Emacs window and restore it. The full buffer must be
redrawn correctly after restore.
Failure: parts of the buffer remain blank or show garbled content.
** TODO 17.6 Rapid buffer switching — no flash
Press =r= to start rapid buffer cycling (10 switches per second).
No flash or blank frame should be visible between switches.
Failure: visible white or black frame appears during cycling.
** TODO 17.7 Simultaneous overlays + hl-line — no interference
Press =x= to add 50 random overlays across the buffer.
hl-line highlighting must still correctly highlight the cursor line.
Failure: hl-line disappears, or overlays cause garbled rendering.

131
qa/17-edge-cases/test.el Normal file
View file

@ -0,0 +1,131 @@
;;; test.el --- Visual QA: Edge Cases & Performance -*- lexical-binding: t; -*-
;;
;; GPU: emacs --no-init-file -l qa/17-edge-cases/test.el
;; Vanilla: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/17-edge-cases/test.el
;;
;; Keys: l=large buffer, f=font stress, m=dark theme, o=light theme,
;; r=rapid cycling, x=overlays, q=stop cycling
(defvar qa-edge--cycle-timer nil)
(defun qa-edge-large-buffer ()
(interactive)
(let ((buf (get-buffer-create "*QA: Large Buffer*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(dotimes (i 5000)
(insert (format "%5d │ " (1+ i)))
(cond
((zerop (mod i 10))
(insert (propertize "CHECKPOINT — bold blue marker\n"
'face '(:weight bold :foreground "cornflower blue"))))
((zerop (mod i 7))
(insert (propertize "italic line — The quick brown fox\n"
'face '(:slant italic))))
(t
(insert "The quick brown fox jumps over the lazy dog 1234567890\n"))))
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf)
(message "Large buffer ready — scroll with C-v/M-v")))
(defun qa-edge-font-stress ()
(interactive)
(let ((buf (get-buffer-create "*QA: Font Stress*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(insert (propertize "Font stress test — many sizes and weights\n\n"
'face '(:weight bold)))
(dolist (height '(0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.4 1.6 1.8 2.0 2.5))
(insert (propertize (format "Height %.1f: The quick brown fox ABC 0123\n" height)
'face `(:height ,height))))
(insert "\n")
(dolist (weight '(thin ultralight light normal medium semibold bold extra-bold black))
(insert (propertize (format "Weight %-12s: The quick brown fox\n" weight)
'face `(:weight ,weight))))
(goto-char (point-min))
(setq buffer-read-only t))
(switch-to-buffer buf)
(message "Font stress test — verify all glyphs render correctly")))
(defun qa-edge-dark-theme ()
(interactive)
(when (locate-library "modus-themes")
(load-theme 'modus-vivendi t)
(message "Loaded modus-vivendi (dark)")))
(defun qa-edge-light-theme ()
(interactive)
(when (locate-library "modus-themes")
(load-theme 'modus-operandi t)
(message "Loaded modus-operandi (light)")))
(defun qa-edge-rapid-cycle ()
(interactive)
(let* ((bufs (list (get-buffer-create "*QA: Cycle-A*")
(get-buffer-create "*QA: Cycle-B*")))
(idx 0))
(dolist (b bufs)
(with-current-buffer b
(erase-buffer)
(insert (format "Rapid cycle buffer %s\n" (buffer-name b)))
(dotimes (i 20) (insert (format "Line %d\n" i)))))
(setq qa-edge--cycle-timer
(run-with-timer 0 0.1
(lambda ()
(setq idx (mod (1+ idx) 2))
(switch-to-buffer (nth idx bufs)))))))
(defun qa-edge-stop-cycle ()
(interactive)
(when qa-edge--cycle-timer
(cancel-timer qa-edge--cycle-timer)
(setq qa-edge--cycle-timer nil)
(message "Rapid cycling stopped")))
(defun qa-edge-add-overlays ()
(interactive)
(let ((buf (get-buffer-create "*QA: Overlays*")))
(with-current-buffer buf
(erase-buffer)
(setq buffer-read-only nil)
(hl-line-mode 1)
(dotimes (i 60)
(insert (format "Line %3d: The quick brown fox jumps over the lazy dog\n" (1+ i))))
(setq buffer-read-only t)
;; Add 50 overlays at random positions
(dotimes (_ 50)
(let* ((start (+ (point-min) (random (- (point-max) (point-min) 5))))
(end (min (+ start (1+ (random 20))) (point-max)))
(ov (make-overlay start end))
(bg (nth (random 5) '("lemon chiffon" "lavender" "honeydew"
"misty rose" "azure"))))
(overlay-put ov 'face `(:background ,bg)))))
(switch-to-buffer buf)
(message "50 overlays added — verify hl-line still works")))
(let ((buf (get-buffer-create "*QA: Edge Cases*")))
(with-current-buffer buf
(erase-buffer)
(insert (propertize "=== Edge Cases & Performance QA ===\n\n"
'face '(:weight bold :height 1.3)))
(insert (propertize "Keys:\n" 'face '(:weight bold)))
(insert " l — load 5000-line buffer\n")
(insert " f — font size/weight stress test\n")
(insert " m — switch to dark theme (modus-vivendi)\n")
(insert " o — switch to light theme (modus-operandi)\n")
(insert " r — start rapid buffer cycling (10/s)\n")
(insert " q — stop rapid cycling\n")
(insert " x — add 50 overlays (+ hl-line test)\n\n")
(use-local-map (make-sparse-keymap))
(local-set-key "l" #'qa-edge-large-buffer)
(local-set-key "f" #'qa-edge-font-stress)
(local-set-key "m" #'qa-edge-dark-theme)
(local-set-key "o" #'qa-edge-light-theme)
(local-set-key "r" #'qa-edge-rapid-cycle)
(local-set-key "q" #'qa-edge-stop-cycle)
(local-set-key "x" #'qa-edge-add-overlays))
(switch-to-buffer buf))

41
qa/README.org Normal file
View file

@ -0,0 +1,41 @@
#+TITLE: Visual QA — GPU Backend
#+STARTUP: showall
Each subdirectory contains one test category. Inside you will find:
- =README.org= — what to verify and what failure looks like
- =test.el= — loads the same scenario in GPU and vanilla Emacs for side-by-side comparison
* How to run any test
Open two terminals. In the first, run the GPU version:
: emacs --no-init-file -l qa/NN-name/test.el
In the second, run the vanilla baseline (GPU disabled):
: EMACS_GPU_DISABLE=1 emacs --no-init-file -l qa/NN-name/test.el
Place both windows side by side and compare.
Mark items in the README.org as PASS / FAIL / SKIP.
* Test categories
| Dir | What is tested |
|------------------------+---------------------------------------------|
| [[file:01-render-cycle/]] | Present timing, no-flash on open/switch |
| [[file:02-text-glyphs/]] | ASCII, CJK, bold/italic, colors, box-draw |
| [[file:03-compositions/]] | Combining chars, Arabic, Hebrew, BiDi |
| [[file:04-emoji/]] | Color emoji, ZWJ sequences, flags |
| [[file:05-decorations/]] | Underline (5 styles), overline, strike |
| [[file:06-boxes-reliefs/]] | Box outline, raised/sunken relief |
| [[file:07-cursor/]] | 4 cursor types + Metal animation modes |
| [[file:08-fringe/]] | Built-in bitmaps, custom bitmap, background |
| [[file:09-images/]] | PNG/JPEG/SVG/GIF, alpha, scale, slice |
| [[file:10-scroll/]] | scroll_run parity, easings, hscroll |
| [[file:11-bars/]] | mode-line, header, tab-bar, tool-bar |
| [[file:12-windows/]] | Splits, dividers, child frames |
| [[file:13-display-props/]] | Overlays, line-numbers, hl-line, margins |
| [[file:14-major-modes/]] | org-mode, dired, magit, corfu |
| [[file:15-transitions/]] | Buffer crossfade, HiDPI capture |
| [[file:16-video/]] | Inline video (macOS Metal only) |
| [[file:17-edge-cases/]] | 10k-line buffer, atlas overflow, themes |