Files
aego/tests/integration/playdiff_test.go
sanspie 3d25734dfa
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Failing after 1s
ci / checks (push) Failing after 1s
ci / release (push) Skipped
Stage 10d1: add-component requirements, play diff from history, node labels; one sound button on the web
2026-10-22 13:29:11 +03:00

161 lines
4.0 KiB
Go

package integration
import (
"testing"
"aego/core/ids"
"aego/core/mathx"
"aego/editor/core/document"
"aego/editor/core/gizmo"
pd "aego/editor/core/playdiff"
"aego/modules/gameplay"
"aego/scene"
"aego/schema"
)
type playDiffRig struct {
edit, play *document.SceneDocument
enemy ids.NodeID
hitbox *schema.TypeDescriptor
area *schema.FieldDescriptor
}
func playDiffSession(t *testing.T) (*playDiffRig, func(int)) {
t.Helper()
s, pc := rewindSession(t)
m := s.App.Context().Docs
play := m.PlayDoc()
if play == nil {
t.Fatal("no play document")
}
edit := m.Get(play.ID)
ps := play.Scene()
hb, ok := ps.Registry().ByName(gameplay.NameHitbox)
if !ok {
t.Fatal("Hitbox 2D is not registered")
}
area, _ := hb.Field("area")
enemy := ps.Find(ps.Root(), "EnemyLeft")
if !scene.Has[gameplay.Hitbox2D](ps, enemy) {
t.Fatal("EnemyLeft has no Hitbox 2D")
}
tick := func(n int) {
for range n {
pc.Tick(1.0 / 60)
}
}
return &playDiffRig{edit: edit, play: play, enemy: ps.ID(enemy), hitbox: hb, area: area}, tick
}
// gizmoArea drags the enemy hitbox's Area handle in two steps, as a gizmo drag does.
func (r *playDiffRig) gizmoArea(t *testing.T, w float32) mathx.Rect {
t.Helper()
ps := r.play.Scene()
cur, _ := ps.GetProperty(ps.Handle(r.enemy), r.hitbox.ID, r.area.ID)
rect := cur.R
tx := r.play.History.Begin("Edit Area")
e := &gizmo.EditCtx{Scene: ps, Tx: tx, Node: r.enemy}
for _, k := range []float32{0.5, 1} {
next := rect
next.W = rect.W + (w-rect.W)*k
gizmo.SetProperty(e, r.hitbox.ID, r.area, schema.Rect(next))
}
if e.Err != nil {
tx.Abort()
t.Fatal(e.Err)
}
tx.Commit()
rect.W = w
return rect
}
func (r *playDiffRig) setBounce(t *testing.T) {
t.Helper()
ps := r.play.Scene()
td, ok := ps.Registry().ByName(playerType)
if !ok {
t.Fatal("PlayerController is not registered")
}
fd, _ := td.Field("Bounce")
player := ps.ID(ps.Find(ps.Root(), "Player"))
if err := r.play.Props.Set(nil, []ids.NodeID{player}, td.ID, fd.ID, schema.Vec2(mathx.Vec2{X: 0, Y: -14})); err != nil {
t.Fatal(err)
}
}
func TestPlayDiffListsOnlyYourEdits(t *testing.T) {
r, tick := playDiffSession(t)
tick(300)
r.setBounce(t)
want := r.gizmoArea(t, 3)
ps := r.play.Scene()
from := ps.Local(ps.Handle(r.enemy)).Position
tick(60)
if ps.Local(ps.Handle(r.enemy)).Position == from {
t.Fatal("the enemy did not move after the gizmo edit")
}
hist := pd.FromHistory(r.edit, r.play)
if len(hist.Changes) != 2 {
for _, c := range hist.Changes {
t.Logf("row %s %s.%s", c.Name, c.Owner, c.Label)
}
t.Fatalf("history rows = %d, want Bounce and Area", len(hist.Changes))
}
var area *pd.Change
for i := range hist.Changes {
c := &hist.Changes[i]
if !c.Checked || c.Gameplay {
t.Fatalf("row %s.%s should be checked and yours", c.Owner, c.Label)
}
if c.Type == r.hitbox.ID {
area = c
}
}
if area == nil || area.After.R != want || area.Name != "EnemyLeft" {
t.Fatalf("area row %+v, want the gizmo value %v on EnemyLeft", area, want)
}
full := pd.Full(r.edit, r.play, hist)
game := 0
for _, c := range full.Changes {
if c.Gameplay {
if c.Checked {
t.Fatalf("game row %s.%s is checked", c.Owner, c.Label)
}
if c.Node == r.enemy && c.Kind == pd.KindTransform2D {
game++
}
}
}
if game != 1 {
t.Fatalf("the enemy's position is not among the game rows (%d)", game)
}
}
func TestPlayDiffEmptyWithoutEdits(t *testing.T) {
r, tick := playDiffSession(t)
tick(300)
if hist := pd.FromHistory(r.edit, r.play); !hist.Empty() || len(hist.Created)+len(hist.Removed) != 0 {
t.Fatalf("a run with no edits lists %+v", hist)
}
}
func TestPlayDiffDialogOnlyAfterEdits(t *testing.T) {
for _, edited := range []bool{false, true} {
s, pc := rewindSession(t)
for range 120 {
pc.Tick(1.0 / 60)
}
if edited {
m := s.App.Context().Docs
play := m.PlayDoc()
r := &playDiffRig{edit: m.Get(play.ID), play: play}
r.setBounce(t)
}
pc.Stop()
s.Step()
if got := s.App.PlayDiffOpen(); got != edited {
t.Fatalf("edited=%v: play changes dialog open=%v", edited, got)
}
}
}