Files
aego/tests/integration/editor_ops_test.go

231 lines
6.5 KiB
Go

package integration
import (
"aego/editor/core/assetops"
"os"
"path/filepath"
"strings"
"testing"
"aego/app/edit"
"aego/app/run"
"aego/asset/pipeline"
"aego/core/ids"
"aego/core/log"
"aego/editor/core/context"
"aego/editor/shell"
"aego/project"
)
func openEditor(t *testing.T) (*edit.Session, *context.Editor) {
t.Helper()
p, err := project.Create(t.TempDir(), "EditorOps", false)
if err != nil {
t.Fatal(err)
}
s, err := edit.Open(p, edit.Config{Headless: true, NoWatch: true, Logger: log.Nop()})
if err != nil {
t.Fatal(err)
}
t.Cleanup(s.Close)
s.Step()
return s, s.App.Context()
}
func fileExists(abs string) bool {
_, err := os.Stat(abs)
return err == nil
}
func assetAt(t *testing.T, ctx *context.Editor, rel string) ids.AssetID {
t.Helper()
id, ok := ctx.Pipeline.Lookup(rel)
if !ok {
t.Fatalf("%s is not tracked by the pipeline", rel)
}
if !fileExists(ctx.Pipeline.Abs(rel) + pipeline.MetaSuffix) {
t.Fatalf("%s has no meta", rel)
}
return id
}
func readRel(t *testing.T, ctx *context.Editor, rel string) string {
t.Helper()
data, err := os.ReadFile(ctx.Pipeline.Abs(rel))
if err != nil {
t.Fatal(err)
}
return string(data)
}
func TestEditorCreatesFromMenu(t *testing.T) {
_, ctx := openEditor(t)
if err := ctx.NewScene("scenes/level.aescene"); err != nil {
t.Fatal(err)
}
assetAt(t, ctx, "scenes/level.aescene")
if ctx.Docs.Find("scenes/level.aescene") == nil || ctx.Doc() == nil || ctx.Doc().Path != "scenes/level.aescene" {
t.Fatal("new scene must be open and active")
}
prefab := "prefabs/crate" + assetops.PrefabExt
if err := ctx.NewPrefab(prefab); err != nil {
t.Fatal(err)
}
assetAt(t, ctx, prefab)
rel, err := assetops.CreateScript(ctx, "scripts/enemy", "Enemy")
if err != nil {
t.Fatal(err)
}
assetAt(t, ctx, rel)
src := readRel(t, ctx, rel)
if !strings.HasPrefix(src, "package enemy\n") || !strings.Contains(src, "script Enemy {") {
t.Fatalf("script source:\n%s", src)
}
}
func TestEditorFileOpsKeepIDs(t *testing.T) {
_, ctx := openEditor(t)
src := filepath.Join(t.TempDir(), "hero.png")
if err := os.WriteFile(src, testPNG(t, 16, 16), 0o644); err != nil {
t.Fatal(err)
}
created, batch, err := ctx.Pipeline.ImportExternal([]string{src}, "assets/chars")
ctx.ApplyBatch(batch)
if err != nil || len(created) != 1 {
t.Fatalf("import: %v %v", created, err)
}
tex := created[0]
var ops assetops.Ops
if err := ops.Apply(ctx, []assetops.FileOp{{Dir: true, From: "assets/chars", To: "assets/heroes"}}); err != nil {
t.Fatal(err)
}
if id := assetAt(t, ctx, "assets/heroes/hero.png"); id != tex {
t.Fatal("move changed the asset id")
}
if e, ok := ctx.Pipeline.Entry(tex); !ok || e.Missing || e.Source != "assets/heroes/hero.png" {
t.Fatalf("entry after move = %+v", e)
}
if err := ops.Undo(ctx); err != nil {
t.Fatal(err)
}
assetAt(t, ctx, "assets/chars/hero.png")
if err := ops.Apply(ctx, []assetops.FileOp{{ID: tex, From: "assets/chars/hero.png", To: "assets/chars/knight.png"}}); err != nil {
t.Fatal(err)
}
if id := assetAt(t, ctx, "assets/chars/knight.png"); id != tex {
t.Fatal("rename changed the asset id")
}
dup, err := assetops.Duplicate(ctx, tex)
if err != nil {
t.Fatal(err)
}
if id := assetAt(t, ctx, "assets/chars/knight copy.png"); id != dup || dup == tex {
t.Fatal("duplicate must land next to the source with a fresh id")
}
if err := assetops.Delete(ctx, []ids.AssetID{dup}); err != nil {
t.Fatal(err)
}
abs := ctx.Pipeline.Abs("assets/chars/knight copy.png")
if fileExists(abs) || fileExists(abs+pipeline.MetaSuffix) {
t.Fatal("delete must remove the file and its meta")
}
}
func TestEditorScriptMoveRetargetsPackage(t *testing.T) {
_, ctx := openEditor(t)
rel, err := assetops.CreateScript(ctx, "scripts/enemy", "Enemy")
if err != nil {
t.Fatal(err)
}
id := assetAt(t, ctx, rel)
if err := ctx.Pipeline.Mkdir("scripts/shared"); err != nil {
t.Fatal(err)
}
var ops assetops.Ops
dst := "scripts/shared/enemy.aes"
if err := ops.Apply(ctx, []assetops.FileOp{{ID: id, From: rel, To: dst}}); err != nil {
t.Fatal(err)
}
if src := readRel(t, ctx, dst); !strings.HasPrefix(src, "package shared\n") {
t.Fatalf("package not updated:\n%s", src)
}
if n, ok := ctx.LastNotice(); !ok || n.Level != context.LevelWarn || !strings.Contains(n.Text, "package shared") {
t.Fatalf("move must warn about imports: %+v", n)
}
if err := ops.Apply(ctx, []assetops.FileOp{{Dir: true, From: "scripts/shared", To: "scripts/common"}}); err != nil {
t.Fatal(err)
}
if src := readRel(t, ctx, "scripts/common/enemy.aes"); !strings.HasPrefix(src, "package common\n") {
t.Fatalf("package folder rename must rewrite the package:\n%s", src)
}
}
func TestEditorSceneSaveAsStartupClose(t *testing.T) {
s, ctx := openEditor(t)
if err := ctx.NewScene("scenes/a.aescene"); err != nil {
t.Fatal(err)
}
d := ctx.Docs.Find("scenes/a.aescene")
assetops.PromptSaveAs(ctx, d)
qs := ctx.TakeQuestions()
if len(qs) != 1 || qs[0].Submit == nil {
t.Fatalf("save as must ask for a path: %+v", qs)
}
qs[0].Submit([]string{"scenes/b"})
if d.Path != "scenes/b.aescene" || !fileExists(ctx.Pipeline.Abs(d.Path)) {
t.Fatalf("save as wrote %s", d.Path)
}
if _, _, err := ctx.Pipeline.Scan(); err != nil {
t.Fatal(err)
}
id := assetAt(t, ctx, d.Path)
assetops.SetStartup(ctx, id, d.Path)
back, err := project.Load(ctx.Project.Root)
if err != nil || back.MainScene != id {
t.Fatalf("startup scene not saved: %v", err)
}
d.MarkDirty()
ctx.Commands.Run(shell.CommandCloseScene)
s.Step()
if ctx.Docs.Find(d.Path) == nil {
t.Fatal("a dirty scene must not close without the dialog")
}
}
func TestEditorCreatesDefaultDirs(t *testing.T) {
p, err := project.Create(t.TempDir(), "Dirs", false)
if err != nil {
t.Fatal(err)
}
scenePath := filepath.Join(p.ScenesDir(), "main.aescene")
if err := os.WriteFile(scenePath, []byte(rotatorScene), 0o644); err != nil {
t.Fatal(err)
}
gone := []string{"scripts", "prefabs"}
for _, d := range gone {
if err := os.RemoveAll(filepath.Join(p.Root, d)); err != nil {
t.Fatal(err)
}
}
app, err := run.Open(p, run.Config{Headless: true, Logger: log.Nop(), ScenePath: scenePath})
if err != nil {
t.Fatal(err)
}
app.Close()
for _, d := range gone {
if fileExists(filepath.Join(p.Root, d)) {
t.Fatalf("aego run created %s", d)
}
}
s, err := edit.Open(p, edit.Config{Headless: true, NoWatch: true, Logger: log.Nop()})
if err != nil {
t.Fatal(err)
}
s.Close()
for _, d := range project.DefaultDirs {
if !fileExists(filepath.Join(p.Root, d)) {
t.Fatalf("editor did not create %s", d)
}
}
}