129 lines
4.0 KiB
Go
129 lines
4.0 KiB
Go
package art
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"testing"
|
|
)
|
|
|
|
func TestEffectTemplatesAreEditableAnimatedSources(t *testing.T) {
|
|
if len(EffectTemplates()) != 8 {
|
|
t.Fatal("the authoring catalogue must expose all eight promised effects")
|
|
}
|
|
seen := map[string]bool{}
|
|
for _, template := range EffectTemplates() {
|
|
t.Run(template.ID, func(t *testing.T) {
|
|
if seen[template.ID] || template.NameEN == "" || template.NameRU == "" || template.ParticlePreset == "" {
|
|
t.Fatal("duplicate or incomplete localized template")
|
|
}
|
|
seen[template.ID] = true
|
|
data, err := EffectTemplateBytes(template.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
doc, err := Decode(data, Limits{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer doc.Close()
|
|
info := doc.Info()
|
|
if info.Properties[EffectTemplateProperty] != template.ID || info.Canvas.Width != 32 || info.Canvas.Height != 32 || len(info.Frames) != 8 || len(info.Layers) < 2 {
|
|
t.Fatalf("not a complete, editable starter: %+v", info)
|
|
}
|
|
if len(info.Tags) != 1 || info.Tags[0].From != info.Frames[0].ID || info.Tags[0].To != info.Frames[7].ID || info.Tags[0].Direction != Forward {
|
|
t.Fatal("starter lost its full-range playback tag")
|
|
}
|
|
frames := map[[32]byte]bool{}
|
|
var emissionPixels int
|
|
for _, frame := range info.Frames {
|
|
if frame.DurationMS != 100 {
|
|
t.Fatal("starter must have a uniform 10 FPS flipbook timing")
|
|
}
|
|
if pivot, ok := info.FramePivot(frame.ID); !ok || pivot.X < 0 || pivot.Y < 0 || pivot.X >= 32 || pivot.Y >= 32 {
|
|
t.Fatal("starter has no usable pivot")
|
|
}
|
|
pixels, err := doc.Render(frame.ID, RenderOptions{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
opaque := 0
|
|
for i := 3; i < len(pixels.Pix); i += 4 {
|
|
if pixels.Pix[i] != 0 {
|
|
opaque++
|
|
}
|
|
}
|
|
if opaque < 5 || opaque > 700 {
|
|
t.Fatalf("frame is empty or lacks a transparent silhouette: %d", opaque)
|
|
}
|
|
frames[sha256.Sum256(pixels.Pix)] = true
|
|
emission, err := doc.Render(frame.ID, RenderOptions{Channel: Emission})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for i := 0; i < len(emission.Pix); i += 4 {
|
|
if emission.Pix[i+3] > 0 && (emission.Pix[i] > 0 || emission.Pix[i+1] > 0 || emission.Pix[i+2] > 0) {
|
|
emissionPixels++
|
|
}
|
|
}
|
|
}
|
|
if len(frames) < 4 {
|
|
t.Fatal("effect must animate its artwork, not repeat a static image")
|
|
}
|
|
expectEmission := template.ID != "dust" && template.ID != "smoke"
|
|
if (emissionPixels > 0) != expectEmission {
|
|
t.Fatalf("incorrect emission channel for %s", template.ID)
|
|
}
|
|
encoded, err := doc.Encode()
|
|
if err != nil || !bytes.Equal(data, encoded) {
|
|
t.Fatalf("starter does not round-trip canonically: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewEffectsHaveIndependentIdentityAndPixels(t *testing.T) {
|
|
a, err := NewEffectDocument("fire", Limits{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer a.Close()
|
|
b, err := NewEffectDocument("fire", Limits{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer b.Close()
|
|
if a.Info().ID == b.Info().ID {
|
|
t.Fatal("New Effect reused document identity")
|
|
}
|
|
untouched, _ := b.Encode()
|
|
starter, _ := EffectTemplateBytes("fire")
|
|
cel := a.Info().Cels[0].ID
|
|
e, _ := a.Begin(a.Revision(), "edit this instance")
|
|
if err := e.SetPixel(cel, 0, 0, Paint{Color: Color{1, 2, 3, 255}}, nil, false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
c, err := e.Prepare()
|
|
if err != nil || c == nil {
|
|
t.Fatalf("edit: %v", err)
|
|
}
|
|
if err := c.Apply(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
after, _ := b.Encode()
|
|
reference, _ := EffectTemplateBytes("fire")
|
|
if !bytes.Equal(untouched, after) || !bytes.Equal(starter, reference) {
|
|
t.Fatal("editing an instance changed another document or the template")
|
|
}
|
|
catalogue := EffectTemplates()
|
|
catalogue[0].ID = "changed"
|
|
if _, ok := EffectTemplateOf("dust"); !ok {
|
|
t.Fatal("caller mutated the shared catalogue")
|
|
}
|
|
if _, err := NewEffectDocument("../fire", Limits{}); err == nil {
|
|
t.Fatal("template IDs were treated as arbitrary paths")
|
|
}
|
|
if _, err := NewEffectDocument("fire", Limits{MaxFrames: 4}); err == nil {
|
|
t.Fatal("template creation bypassed document limits")
|
|
}
|
|
}
|