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:
parent
8955c39a9b
commit
74a4e6c5cc
36 changed files with 2000 additions and 517 deletions
517
VISUAL-QA.org
517
VISUAL-QA.org
|
|
@ -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.
|
||||
38
qa/01-render-cycle/README.org
Normal file
38
qa/01-render-cycle/README.org
Normal 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 1–2 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.
|
||||
29
qa/01-render-cycle/test.el
Normal file
29
qa/01-render-cycle/test.el
Normal 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))
|
||||
45
qa/02-text-glyphs/README.org
Normal file
45
qa/02-text-glyphs/README.org
Normal 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 1–2px 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 1–2px 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
78
qa/02-text-glyphs/test.el
Normal 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))
|
||||
30
qa/03-compositions/README.org
Normal file
30
qa/03-compositions/README.org
Normal 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.
|
||||
59
qa/03-compositions/test.el
Normal file
59
qa/03-compositions/test.el
Normal 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
37
qa/04-emoji/README.org
Normal 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
53
qa/04-emoji/test.el
Normal 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))
|
||||
54
qa/05-decorations/README.org
Normal file
54
qa/05-decorations/README.org
Normal 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
75
qa/05-decorations/test.el
Normal 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))
|
||||
39
qa/06-boxes-reliefs/README.org
Normal file
39
qa/06-boxes-reliefs/README.org
Normal 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.
|
||||
81
qa/06-boxes-reliefs/test.el
Normal file
81
qa/06-boxes-reliefs/test.el
Normal 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
53
qa/07-cursor/README.org
Normal 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
69
qa/07-cursor/test.el
Normal 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
39
qa/08-fringe/README.org
Normal 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
63
qa/08-fringe/test.el
Normal 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
50
qa/09-images/README.org
Normal 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
84
qa/09-images/test.el
Normal 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
49
qa/10-scroll/README.org
Normal 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 2–3 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
67
qa/10-scroll/test.el
Normal 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
48
qa/11-bars/README.org
Normal 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
50
qa/11-bars/test.el
Normal 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
42
qa/12-windows/README.org
Normal 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
70
qa/12-windows/test.el
Normal 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))
|
||||
54
qa/13-display-props/README.org
Normal file
54
qa/13-display-props/README.org
Normal 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.
|
||||
80
qa/13-display-props/test.el
Normal file
80
qa/13-display-props/test.el
Normal 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))
|
||||
43
qa/14-major-modes/README.org
Normal file
43
qa/14-major-modes/README.org
Normal 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
88
qa/14-major-modes/test.el
Normal 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))
|
||||
51
qa/15-transitions/README.org
Normal file
51
qa/15-transitions/README.org
Normal 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
59
qa/15-transitions/test.el
Normal 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
51
qa/16-video/README.org
Normal 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
57
qa/16-video/test.el
Normal 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))
|
||||
43
qa/17-edge-cases/README.org
Normal file
43
qa/17-edge-cases/README.org
Normal 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
131
qa/17-edge-cases/test.el
Normal 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
41
qa/README.org
Normal 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 |
|
||||
Loading…
Add table
Reference in a new issue