uitest

package
v0.2.14 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package uitest provides test fixtures shared by this module's test suites: synthetic image files in every format the viewer reads, the temp files and URIs to hand them over by, and swap-in stubs for the OS-level seams (file chooser, image clipboard).

It exists because Go can't share unexported test helpers across packages, and the previous answer to that - copying `writeTempFile`/`encodeJPEG` into each package that needed them - doesn't scale to the per-feature package split this module is working through. Everything here is deliberately viewer-free: fixtures build bytes, files, and URIs, and know nothing about the app's own types.

What stays behind, in each package's own _test.go files, is anything that reads unexported state: the viewer's channel/WaitGroup wait helpers (waitUntilLoaded, settleToast, settleThumbs, ...) synchronize on private fields, and keeping them private is what stops those sync primitives from leaking into an exported API.

Test-only code in a non-test file is intentional (the same shape as the standard library's net/http/httptest): only _test.go files import this, so it never reaches a production binary.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApproxEqual

func ApproxEqual(a, b float32) bool

ApproxEqual reports whether two float32s are within 0.01 of each other - the tolerance layout assertions need, since Fyne's sizing math accumulates rounding noise well below one canvas point.

func CaptureDateJPEG

func CaptureDateJPEG(t *testing.T, w, h int, raw string) []byte

CaptureDateJPEG builds a minimal encoded JPEG carrying a single Exif DateTime tag (0x0132) set to raw ("YYYY:MM:DD HH:MM:SS") - just enough for imaging.CaptureDate, which the capture-date sort mode relies on, to read a timestamp back, without needing a real camera-shot fixture.

func EncodeAnimatedGIF

func EncodeAnimatedGIF(t *testing.T, w, h int, colors []color.Color, delays []int) []byte

EncodeAnimatedGIF builds a multi-frame GIF, one solid-color w x h frame per entry in colors, with the matching delay (in 1/100ths of a second, gif.GIF's native unit) from delays.

func EncodeGIF

func EncodeGIF(t *testing.T, w, h int, c color.Color) []byte

EncodeGIF returns a single-frame solid-color w x h GIF.

func EncodeJPEG

func EncodeJPEG(t *testing.T, w, h int, c color.Color) []byte

EncodeJPEG returns a solid-color w x h JPEG.

func EncodePNG

func EncodePNG(t *testing.T, w, h int, c color.Color) []byte

EncodePNG returns a solid-color w x h PNG.

func EncodeRAWPreview added in v0.2.0

func EncodeRAWPreview(t *testing.T, p RAWPreview) []byte

EncodeRAWPreview builds a little-endian TIFF whose only image is a JPEG stored via JPEGInterchangeFormat (0x0201) and Compression=6 - the shape camera RAW files use for their embedded preview. golang.org/x/image/tiff does not decode compression 6, so the file is not a displayable TIFF: imaging has to extract the JPEG to show anything.

func GPSJPEG added in v0.1.8

func GPSJPEG(t *testing.T, w, h int, lat, lon float64) []byte

GPSJPEG builds a minimal encoded JPEG carrying an Exif GPS sub-IFD (the 0x8825 pointer in IFD0, then the latitude/longitude reference and degrees/minutes/seconds tags) for the given signed decimal degrees - enough for imaging.ReadMetadata to read a position back, and so for the EXIF window's map section to have somewhere to point.

func LineArtGray added in v0.2.3

func LineArtGray(edge, seed int) *image.Gray

LineArtGray draws thin dark strokes on a white background: a sketch, a screenshot, a logo, a scan - the common case where the subject occupies only a small fraction of the pixels. seed picks the stroke positions, so two seeds are two unrelated pictures.

This is the fixture shape that caught the duplicate-detection bug, where reducing such an image to the dHash grid by sampling a few pixels per cell landed on the background nearly every time and reported a near-empty hash. PatternedJPEGURI's dense gradient cannot show that: it has content in every cell.

func LineArtJPEGURI added in v0.2.3

func LineArtJPEGURI(t *testing.T, name string, seed int) fyne.URI

LineArtJPEGURI writes a LineArtGray image to a temp file and returns its URI, mirroring PatternedJPEGURI.

func PatternedJPEGURI added in v0.2.3

func PatternedJPEGURI(t *testing.T, name string, seed int) fyne.URI

PatternedJPEGURI writes a seeded 64×48 grayscale JPEG. Solid-color JPEGs all dHash to 0, so hide-duplicates tests need patterned pixels to tell "same shot" from "different shot".

func PatternedJPEGURISize added in v0.2.8

func PatternedJPEGURISize(t *testing.T, name string, seed, w, h int) fyne.URI

PatternedJPEGURISize is PatternedJPEGURI at an explicit size. Same seed at two sizes is the hide-duplicates fixture for "same shot, different resolution": coordinates are nearest-neighbour mapped onto the 64x48 pattern so a 192x144 file is an upscale of the 64x48 original, not a steeper pixel-space gradient (which dHash treats as a different picture).

func SVGBytes added in v0.1.7

func SVGBytes(w, h int) []byte

SVGBytes builds a synthetic SVG with the given viewBox and a filled rect covering it, so a rasterization of it has visibly non-zero pixels.

func StubChooser

func StubChooser(t *testing.T, out []byte, err error)

StubChooser makes filepicker.Choose return out/err instead of opening the OS file browser.

func StubClipboardCopy

func StubClipboardCopy(t *testing.T, fn func(data []byte) error)

StubClipboardCopy makes clipboard.CopyImage call fn instead of shelling out to the OS clipboard.

func StubClipboardCopyFiles

func StubClipboardCopyFiles(t *testing.T, fn func(paths []string) error)

StubClipboardCopyFiles makes clipboard.CopyFiles call fn instead of shelling out to the OS clipboard - the file-reference twin of StubClipboardCopy, for the grid's batch copy.

func StubSaveChooser

func StubSaveChooser(t *testing.T, fn func(suggestedPath string) ([]byte, error))

StubSaveChooser makes filepicker.ChooseSave call fn instead of opening the OS save panel. It takes a function rather than a fixed result the way StubChooser does, since a caller usually wants to assert on the suggested path it was offered as well as control what comes back.

func StubTrashMove

func StubTrashMove(t *testing.T, fn func(path string) error)

StubTrashMove makes trash.Move call fn instead of shelling out to the OS's real trash/recycle-bin mover.

func StubWallpaperSet

func StubWallpaperSet(t *testing.T, fn func(path string) error)

StubWallpaperSet makes wallpaper.Set call fn instead of changing the machine's real desktop wallpaper - the one stub here whose absence a test run would leave visibly behind on the developer's own screen.

func TempDirJPEGURIs added in v0.2.8

func TempDirJPEGURIs(t *testing.T, names ...string) []fyne.URI

TempDirJPEGURIs writes solid-color 8×8 white JPEGs named names into a single temp directory and returns their file URIs in the same order. TempJPEGURI cannot be used for sibling tests: each call uses its own t.TempDir(), so the files would not share a parent.

func TempGPSJPEGURI added in v0.1.8

func TempGPSJPEGURI(t *testing.T, name string, w, h int, lat, lon float64) fyne.URI

TempGPSJPEGURI writes GPSJPEG's output to a temp file and returns its URI, mirroring TempJPEGURI.

func TempJPEGURI

func TempJPEGURI(t *testing.T, name string, w, h int, c color.Color) fyne.URI

TempJPEGURI writes a solid-color w x h JPEG to a temp file and returns its file URI - the one-liner behind most of this suite's "give me an image to drop" setup.

func TempRAWURI added in v0.2.0

func TempRAWURI(t *testing.T, name string, w, h int, c color.Color) fyne.URI

TempRAWURI writes EncodeRAWPreview to a temp file named name (typically with a RAW extension such as .cr2) and returns its file URI.

func TempSVGURI added in v0.1.7

func TempSVGURI(t *testing.T, name string, w, h int) fyne.URI

TempSVGURI writes SVGBytes to a temp file and returns its URI, mirroring TempJPEGURI.

func TruncatedPNGHeader

func TruncatedPNGHeader(t *testing.T, width, height uint32) []byte

TruncatedPNGHeader builds a PNG file containing only the 8-byte signature and a single, correctly-checksummed IHDR chunk declaring width x height - no IDAT/IEND, so it's useless for a full decode but perfectly readable by image.DecodeConfig, which for a non-paletted color type stops as soon as IHDR has been parsed. Used to prove imaging.ReadAndProbe/LoadImage reject an invalid declared size from the header alone, without needing the rest of the file, and to exercise the viewer's end-to-end handling of that same rejection.

func WriteTempFile

func WriteTempFile(t *testing.T, name string, data []byte) string

WriteTempFile writes data to a uniquely-named file in the test's own temp directory and returns its path. The directory is cleaned up by testing.

Types

type FakeURI

type FakeURI struct {
	FileName, Ext, Mime string
}

FakeURI is a minimal fyne.URI so tests can control extension and MIME type independently, without touching the filesystem. Its Path is "/"+FileName under the file scheme: storage.Parent/List on an image FakeURI can walk the real filesystem root. Use TempJPEGURI (or a real file) whenever a directory listing might run.

func (FakeURI) Authority

func (f FakeURI) Authority() string

func (FakeURI) Extension

func (f FakeURI) Extension() string

func (FakeURI) Fragment

func (f FakeURI) Fragment() string

func (FakeURI) MimeType

func (f FakeURI) MimeType() string

func (FakeURI) Name

func (f FakeURI) Name() string

func (FakeURI) Path

func (f FakeURI) Path() string

func (FakeURI) Query

func (f FakeURI) Query() string

func (FakeURI) Scheme

func (f FakeURI) Scheme() string

func (FakeURI) String

func (f FakeURI) String() string

type RAWPreview added in v0.2.0

type RAWPreview struct {
	Width, Height int
	Color         color.Color
	Orientation   uint16 // 0 omits the tag
	Make, Model   string
	DateTime      string // Exif "YYYY:MM:DD HH:MM:SS"
}

RAWPreview is the options EncodeRAWPreview uses to wrap a JPEG in a TIFF-shaped camera-RAW container. Width/Height/Color build the embedded preview; the optional tags are written into IFD0 so ReadMetadata and orientation correction can see them the way they would on a real CR2/NEF.

type UIQueue added in v0.2.3

type UIQueue struct {
	// contains filtered or unexported fields
}

UIQueue collects the callbacks a background goroutine hands over for the UI goroutine to run, and runs them on whoever calls Drain.

It exists because Fyne's test driver is not a marshaling point: its DoFromGoroutine calls the function inline on the calling goroutine, so a worker's fyne.Do body runs *on the worker*, concurrently with the test goroutine that spawned it. Under -race that is a genuine data race on every widget and every unsynchronized field the body touches - the same code being perfectly safe in the app, where the real driver queues the callback onto the one UI goroutine.

A feature that hands its completions to a UIQueue gets those production semantics back under test: the callback is deferred, and runs serialized on the goroutine that drains it - which for a test is the test goroutine itself, at a point of its own choosing.

The zero value is ready to use. Do is safe from any goroutine; Drain is for one goroutine at a time, the one the test is running on.

func (*UIQueue) Do added in v0.2.3

func (q *UIQueue) Do(f func())

Do queues f, and never runs it on the calling goroutine. A nil f queues fine and panics whoever eventually Drains it - the same way fyneQueue's Do(nil) panics inside the real driver, so this seam doesn't quietly swallow a caller bug the app wouldn't.

func (*UIQueue) Drain added in v0.2.3

func (q *UIQueue) Drain() bool

Drain runs everything queued so far, in the order it was queued, on the calling goroutine, and reports whether it ran anything.

The lock is dropped before the callbacks run: a callback may queue more work, directly or by spawning a worker that does, and holding the lock across one would deadlock. Work queued during a Drain therefore lands in the *next* one, so a caller that needs quiescence loops until Drain reports false.

The batch is already detached from pending by the time the loop below runs it, so a callback that panics aborts every callback still queued behind it in that same batch - they are simply never reached, not lost to a corrupted pending slice.

func (*UIQueue) Len added in v0.2.3

func (q *UIQueue) Len() int

Len is how many callbacks are waiting for a Drain. No current adopter's tests assert on it - internal/ui/grid's asserts on the drained effect instead - but it's here for whichever of the other packages with the same latent worker-races-the-test-goroutine exposure (deletion, slideshow, favorites, exifwin, spiral, toast) adopts this queue next, in case one of them wants to show a worker deferred its paint rather than running it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL