icns

Easily create .icns files (Mac Icons) with this Go library or the included CLI.
Log | Files | Refs | LICENSE

main_test.go (3864B)


      1 package main
      2 
      3 import (
      4 	"bytes"
      5 	"image"
      6 	"image/color"
      7 	"image/png"
      8 	"os"
      9 	"path/filepath"
     10 	"testing"
     11 
     12 	"gioui.org/gpu/headless"
     13 	l "gioui.org/layout"
     14 	"gioui.org/op"
     15 	"gioui.org/unit"
     16 	m "gioui.org/widget/material"
     17 	"github.com/jackmordaunt/icns/v4"
     18 )
     19 
     20 // iconColour is what the test icon is painted, chosen so it cannot be
     21 // confused with the theme's greys.
     22 var iconColour = color.NRGBA{R: 0xFF, G: 0x20, B: 0x20, A: 0xFF}
     23 
     24 // testIcons encodes a flat coloured icns and decodes it back, the same route
     25 // the window takes when it opens a file.
     26 func testIcons(t *testing.T) []image.Image {
     27 	t.Helper()
     28 	src := image.NewNRGBA(image.Rect(0, 0, 512, 512))
     29 	for i := 0; i < len(src.Pix); i += 4 {
     30 		src.Pix[i] = iconColour.R
     31 		src.Pix[i+1] = iconColour.G
     32 		src.Pix[i+2] = iconColour.B
     33 		src.Pix[i+3] = iconColour.A
     34 	}
     35 	var buf bytes.Buffer
     36 	if err := icns.Encode(&buf, src); err != nil {
     37 		t.Fatalf("encoding test icns: %v", err)
     38 	}
     39 	imgs, err := icns.DecodeAll(&buf)
     40 	if err != nil {
     41 		t.Fatalf("decoding test icns: %v", err)
     42 	}
     43 	return imgs
     44 }
     45 
     46 const (
     47 	frameWidth  = 800
     48 	frameHeight = 600
     49 )
     50 
     51 // render lays the window out and rasterises it offscreen, returning how many
     52 // pixels of the icon's colour reached the preview area on the right.
     53 func render(t *testing.T, window *headless.Window, ui *UI) (int, *image.RGBA) {
     54 	t.Helper()
     55 	var ops op.Ops
     56 	gtx := l.Context{
     57 		Ops:         &ops,
     58 		Metric:      unit.Metric{PxPerDp: 1, PxPerSp: 1},
     59 		Constraints: l.Exact(image.Pt(frameWidth, frameHeight)),
     60 	}
     61 	ui.Update(gtx)
     62 	ui.Layout(gtx)
     63 	if err := window.Frame(gtx.Ops); err != nil {
     64 		t.Fatalf("rendering frame: %v", err)
     65 	}
     66 	shot := image.NewRGBA(image.Rect(0, 0, frameWidth, frameHeight))
     67 	if err := window.Screenshot(shot); err != nil {
     68 		t.Fatalf("reading frame: %v", err)
     69 	}
     70 	var painted int
     71 	for y := 0; y < frameHeight; y++ {
     72 		for x := frameWidth / 2; x < frameWidth; x++ {
     73 			c := color.NRGBAModel.Convert(shot.At(x, y)).(color.NRGBA)
     74 			if c.R > 0xC0 && c.G < 0x60 && c.B < 0x60 && c.A > 0x80 {
     75 				painted++
     76 			}
     77 		}
     78 	}
     79 	return painted, shot
     80 }
     81 
     82 // TestRender rasterises the window offscreen, which is the only way to tell
     83 // that the event and input plumbing still produces a frame.
     84 func TestRender(t *testing.T) {
     85 	window, err := headless.NewWindow(frameWidth, frameHeight)
     86 	if err != nil {
     87 		t.Skipf("headless rendering unavailable: %v", err)
     88 	}
     89 	defer window.Release()
     90 
     91 	// An empty window shows the open button and none of the icon's colour,
     92 	// which is what makes the count below mean something.
     93 	empty := &UI{Th: m.NewTheme(), ProcessedIcon: make(chan ProcessedIconResult, 1)}
     94 	if painted, shot := render(t, window, empty); painted > 100 {
     95 		t.Errorf("an empty window drew %d pixels of the icon's colour", painted)
     96 		if path := keep(t, shot); path != "" {
     97 			t.Logf("frame written to %s", path)
     98 		}
     99 	}
    100 
    101 	ui := &UI{Th: m.NewTheme(), ProcessedIcon: make(chan ProcessedIconResult, 1)}
    102 	// Hand the window a loaded file the way the loader goroutine does, so
    103 	// Update drains it into thumbnails.
    104 	ui.ProcessedIcon <- ProcessedIconResult{File: "test.icns", Imgs: testIcons(t)}
    105 	painted, shot := render(t, window, ui)
    106 	if len(ui.Icons) == 0 {
    107 		t.Fatal("the loaded icons did not reach the window")
    108 	}
    109 	if ui.Preview == nil {
    110 		t.Fatal("no icon was selected for the preview area")
    111 	}
    112 	t.Logf("icon pixels in the preview area: %d", painted)
    113 	if painted < 1000 {
    114 		t.Errorf("only %d pixels of the icon were drawn in the preview area", painted)
    115 		if path := keep(t, shot); path != "" {
    116 			t.Logf("frame written to %s", path)
    117 		}
    118 	}
    119 }
    120 
    121 // keep writes the frame out so a failure can be looked at.
    122 func keep(t *testing.T, img image.Image) string {
    123 	t.Helper()
    124 	path := filepath.Join(t.TempDir(), "frame.png")
    125 	f, err := os.Create(path)
    126 	if err != nil {
    127 		return ""
    128 	}
    129 	defer f.Close()
    130 	if err := png.Encode(f, img); err != nil {
    131 		return ""
    132 	}
    133 	return path
    134 }