ci-platforms / darwin arm64 smoke (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Failing after 12m19s
ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
github-native / gitea-pending (push) Skipped
github-native / darwin amd64 native (push) Skipped
github-native / linux amd64 native (push) Skipped
github-native / windows amd64 native (push) Skipped
github-native / darwin arm64 native (push) Skipped
github-native / gitea-result (push) Skipped
ci / checks (push) Failing after 23m57s
ci / release (push) Skipped
GitHub / windows amd64 native GitHub-hosted native CI: failure
GitHub / darwin arm64 native GitHub-hosted native CI: failure
GitHub / darwin amd64 native GitHub-hosted native CI: failure
GitHub / linux amd64 native GitHub-hosted native CI: success
160 lines
5.0 KiB
Go
160 lines
5.0 KiB
Go
package assetops
|
|
|
|
import (
|
|
"aego/asset"
|
|
"aego/asset/pipeline"
|
|
"aego/core/ids"
|
|
"aego/core/log"
|
|
"aego/editor/core/context"
|
|
"aego/editor/core/document"
|
|
"aego/project"
|
|
"bytes"
|
|
"errors"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
type sourceImporter struct{}
|
|
|
|
func (sourceImporter) ID() string { return "source" }
|
|
func (sourceImporter) Version() int { return 1 }
|
|
func (sourceImporter) Extensions() []string { return []string{".aeshader", ".aemat"} }
|
|
func (sourceImporter) Defaults() pipeline.Settings { return pipeline.Settings{} }
|
|
func (sourceImporter) Import(c *pipeline.ImportContext) (*pipeline.Result, error) {
|
|
if bytes.Equal(c.Source, []byte("reject")) {
|
|
return nil, errors.New("invalid source")
|
|
}
|
|
return &pipeline.Result{Kind: asset.ArtShaderInclude, Artifacts: []pipeline.Produced{{Kind: asset.ArtShaderInclude, Data: c.Source}}}, nil
|
|
}
|
|
|
|
func sourceContext(t *testing.T) *context.Editor {
|
|
t.Helper()
|
|
p, err := project.Create(t.TempDir(), "files", false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
reg := pipeline.NewImporterRegistry()
|
|
if err = reg.Register(sourceImporter{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pl, err := pipeline.Open(p, reg, log.Nop())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(pl.Close)
|
|
return &context.Editor{Project: p, Pipeline: pl, Docs: document.NewManager(document.Options{Root: p.Root})}
|
|
}
|
|
|
|
func TestCreateMaterialAndShaderIsOneUndoPreservingIDs(t *testing.T) {
|
|
ctx := sourceContext(t)
|
|
var o Ops
|
|
var a, b ids.AssetID
|
|
err := o.Transaction(ctx, func() error {
|
|
var e error
|
|
a, e = o.CreateFile(ctx, "assets/source.aeshader", []byte("shader"))
|
|
if e != nil {
|
|
return e
|
|
}
|
|
b, e = o.CreateFile(ctx, "assets/material.aemat", []byte("material "+a.String()))
|
|
return e
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ma, _ := os.ReadFile(ctx.Pipeline.Abs("assets/source.aeshader.meta"))
|
|
mb, _ := os.ReadFile(ctx.Pipeline.Abs("assets/material.aemat.meta"))
|
|
if err = o.Undo(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if o.CanUndo() || !o.CanRedo() || Exists(ctx, "assets/source.aeshader") || Exists(ctx, "assets/material.aemat") {
|
|
t.Fatal("two-file creation was not one complete undo")
|
|
}
|
|
if err = o.Redo(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ai, _ := ctx.Pipeline.Lookup("assets/source.aeshader")
|
|
bi, _ := ctx.Pipeline.Lookup("assets/material.aemat")
|
|
ra, _ := os.ReadFile(ctx.Pipeline.Abs("assets/source.aeshader.meta"))
|
|
rb, _ := os.ReadFile(ctx.Pipeline.Abs("assets/material.aemat.meta"))
|
|
if ai != a || bi != b || !bytes.Equal(ma, ra) || !bytes.Equal(mb, rb) {
|
|
t.Fatal("redo changed asset identity or import settings")
|
|
}
|
|
// Batch preflight must refuse before deleting either source.
|
|
d, e := ctx.Docs.OpenScript("assets/source.aeshader")
|
|
if e != nil {
|
|
t.Fatal(e)
|
|
}
|
|
d.Code.Replace(0, 0, "edited ")
|
|
if err = o.Undo(ctx); err == nil {
|
|
t.Fatal("undo deleted unsaved source")
|
|
}
|
|
if !Exists(ctx, "assets/source.aeshader") || !Exists(ctx, "assets/material.aemat") || !o.CanUndo() {
|
|
t.Fatal("rejected undo partially changed files/history")
|
|
}
|
|
d.Code.Undo()
|
|
if err = o.Undo(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestCreateTransactionRollsBackAndRejectsEscape(t *testing.T) {
|
|
ctx := sourceContext(t)
|
|
var o Ops
|
|
rejected := errors.New("stop")
|
|
err := o.Transaction(ctx, func() error {
|
|
_, e := o.CreateFile(ctx, "assets/test.aeshader", []byte("one"))
|
|
if e != nil {
|
|
return e
|
|
}
|
|
return rejected
|
|
})
|
|
if !errors.Is(err, rejected) || Exists(ctx, "assets/test.aeshader") || Exists(ctx, "assets/test.aeshader.meta") || o.CanUndo() {
|
|
t.Fatal("failed transaction left created files or history")
|
|
}
|
|
if _, err = o.CreateFile(ctx, "../escape.aeshader", []byte("no")); err == nil {
|
|
t.Fatal("asset creation escaped project")
|
|
}
|
|
}
|
|
|
|
func TestCreateReportsImportFailureAndKeepsSourceForRepair(t *testing.T) {
|
|
ctx := sourceContext(t)
|
|
var o Ops
|
|
const rel = "assets/broken.aeshader"
|
|
id, err := o.CreateFile(ctx, rel, []byte("reject"))
|
|
if id.IsZero() || err == nil || !strings.Contains(err.Error(), "invalid source") {
|
|
t.Fatalf("expected an import error for the created asset: id=%v err=%v", id, err)
|
|
}
|
|
data, err := os.ReadFile(ctx.Pipeline.Abs(rel))
|
|
if err != nil || string(data) != "reject" {
|
|
t.Fatalf("failed import did not preserve the created source: %q, %v", data, err)
|
|
}
|
|
if !o.CanUndo() {
|
|
t.Fatal("failed import source was not recorded for undo")
|
|
}
|
|
if err := o.Undo(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if Exists(ctx, rel) {
|
|
t.Fatal("undo did not remove the unchanged source created by this operation")
|
|
}
|
|
}
|
|
|
|
func TestFailedImportRollsBackNewFilesInCreationTransaction(t *testing.T) {
|
|
ctx := sourceContext(t)
|
|
var o Ops
|
|
err := o.Transaction(ctx, func() error {
|
|
if _, err := o.CreateFile(ctx, "assets/good.aeshader", []byte("ok")); err != nil {
|
|
return err
|
|
}
|
|
_, err := o.CreateFile(ctx, "assets/bad.aemat", []byte("reject"))
|
|
return err
|
|
})
|
|
if err == nil || !strings.Contains(err.Error(), "invalid source") {
|
|
t.Fatalf("transaction hid the import failure: %v", err)
|
|
}
|
|
if Exists(ctx, "assets/good.aeshader") || Exists(ctx, "assets/bad.aemat") || o.CanUndo() {
|
|
t.Fatal("failed transaction left newly created sources or history")
|
|
}
|
|
}
|