Files
aego/tests/golden/editor_test.go

191 lines
5.2 KiB
Go

//go:build aego_golden
package golden
import (
"image"
"os"
"path/filepath"
"runtime"
"testing"
"aego/app/docstool"
"aego/core/log"
"aego/core/mathx"
"aego/editor/core/context"
"aego/editor/core/services"
edocs "aego/editor/panels/docs"
"aego/engine"
"aego/gpu"
"aego/platform/input"
"aego/render"
renderhost "aego/render/host"
"aego/render/icons"
"aego/render/text"
"aego/render/view"
"aego/ui/core"
"aego/ui/draw"
"aego/ui/theme"
"aego/ui/widgets"
)
const (
editorW = 800
editorH = 520
)
type editorEnv struct {
e *engine.Engine
r *render.Renderer
ui *core.Context
target gpu.TextureHandle
w, h int
}
func newEditorEnv(t *testing.T) *editorEnv {
return newEditorEnvSize(t, editorW, editorH)
}
func newEditorEnvSize(t *testing.T, w, h int) *editorEnv {
t.Helper()
runtime.LockOSThread()
mod := renderhost.New(render.Options{MaxSprites: 65536})
e, err := engine.NewBuilder(engine.Config{
Title: "editor-golden",
Width: w,
Height: h,
Hidden: true,
ResRoot: "../../demos/res",
UserRoot: t.TempDir(),
AppID: "aego-golden-editor",
}).WithLogger(log.Nop()).Use(mod).Build()
if err != nil {
t.Skipf("no GL context available: %v", err)
}
t.Cleanup(func() { e.Close() })
if name := e.Caps().Renderer; !softwareRenderer(name) && os.Getenv("AEGO_GOLDEN_ALLOW_HARDWARE") == "" {
t.Fatalf("editor goldens require a software rasterizer, got %q", name)
}
r := mod.Renderer()
ui := core.New(r.UIFont())
ui.Theme = theme.Editor()
set, err := text.LoadSet(e.VFS(), r.Textures(), "engine://fonts", "ui", []int{12, 14, 16, 20}, []int{1, 2})
if err != nil {
t.Fatal(err)
}
ui.SetFonts(set)
if mono, err := text.LoadSet(e.VFS(), r.Textures(), "engine://fonts", "ui-mono", []int{12, 14, 16, 20}, []int{1, 2}); err == nil {
ui.SetMonoFonts(mono)
}
if icons, err := icons.Load(e.VFS(), r.Textures(), "engine://icons"); err == nil {
ui.Icons = icons
}
if sh, err := draw.NewShapes(e.GPU(), r.Textures()); err == nil {
ui.SetShapes(sh)
}
target, err := e.GPU().CreateTexture(gpu.TextureDesc{Width: w, Height: h, Format: gpu.TexSRGBA8, Label: "editor.golden"})
if err != nil {
t.Fatal(err)
}
return &editorEnv{e: e, r: r, ui: ui, target: target, w: w, h: h}
}
func (env *editorEnv) draw(fn func(full mathx.Rect)) (*image.NRGBA, error) {
ui, r, e := env.ui, env.r, env.e
w, h := env.w, env.h
full := mathx.Rect{W: float32(w), H: float32(h)}
var list *draw.List
for i := 0; i < 2; i++ {
ui.Begin(&input.State{}, mathx.Vec2{X: float32(w), Y: float32(h)}, 1)
ui.Rect(full, ui.Theme.Background)
ui.Region(full)
fn(full)
ui.PopCursor()
list = ui.End()
}
frame := e.GPU().BeginFrame(w, h)
if err := r.BeginFrame(frame, w, h, 1); err != nil {
return nil, err
}
v := view.ScreenView(w, h, 1)
v.Target, v.Clear, v.DoClear, v.Label = env.target, ui.Theme.Background, true, "editor.golden"
f := r.View(v)
white := r.White()
if fnt := ui.Font(); fnt != nil && !fnt.White.IsZero() {
white = fnt.White
}
list.Flush(f, ui.Font(), white, 0)
f.End()
r.EndFrame()
e.GPU().EndFrame()
buf := make([]byte, w*h*4)
if err := e.GPU().ReadPixels(env.target, 0, 0, w, h, buf); err != nil {
return nil, err
}
return &image.NRGBA{Pix: buf, Stride: w * 4, Rect: image.Rect(0, 0, w, h)}, nil
}
func checkGolden(t *testing.T, name string, img *image.NRGBA, err error) {
t.Helper()
if err != nil {
t.Fatal(err)
}
path := filepath.Join("testdata", name+".png")
if *update {
writePNG(t, path, img)
return
}
compare(t, path, img, 8)
}
func TestEditorGolden(t *testing.T) {
env := newEditorEnv(t)
reg, err := docstool.Registry(nil, nil)
if err != nil {
t.Fatal(err)
}
ctx := &context.Editor{UI: env.ui}
for _, tc := range []struct {
name string
ref string
want string
}{
{"editor_docs_coroutines", "language/coroutines", "language/coroutines"},
{"editor_docs_yield", "key:yield", "keyword/yield"},
{"editor_docs_statements", "language/declarations-and-statements", "language/declarations-and-statements"},
{"editor_docs_types", "language/types", "language/types"},
} {
p := edocs.New(reg, ctx, ctx)
p.Show(tc.ref, "")
img, err := env.draw(func(full mathx.Rect) { p.Draw(&services.Frame{UI: env.ui}, full) })
t.Run(tc.name, func(t *testing.T) {
if p.Current() != tc.want {
t.Fatalf("panel shows %q, want %q", p.Current(), tc.want)
}
checkGolden(t, tc.name, img, err)
})
}
}
func TestEditorMenuGolden(t *testing.T) {
env := newEditorEnv(t)
ui := env.ui
id := core.IDOf("golden.menu")
img, err := env.draw(func(full mathx.Rect) {
if !ui.PopupOpen(id) {
ui.OpenPopup(id, mathx.Vec2{X: 40, Y: 40})
}
if ui.BeginPopup(id, 240) {
widgets.Entry(ui, widgets.Item{Label: "Frame Selection", Shortcut: "F", Icon: "view.zoom", Enabled: true})
widgets.Entry(ui, widgets.Item{Label: "Snap", Icon: "view.snap", Enabled: true, Checked: true})
widgets.Separator(ui)
widgets.Entry(ui, widgets.Item{Label: "Delete", Shortcut: "Delete"})
ui.EndPopup()
}
widgets.ButtonRect(ui, ui.ID("btn"), mathx.Rect{X: 320, Y: 40, W: 120, H: 26}, "Button")
v := "field"
widgets.TextFieldRect(ui, ui.ID("fld"), mathx.Rect{X: 320, Y: 80, W: 160, H: 24}, &v, false)
})
checkGolden(t, "editor_menu_popup", img, err)
}