208 lines
5.8 KiB
Go
208 lines
5.8 KiB
Go
package settings
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"aego/format/aetext"
|
|
"aego/schema"
|
|
)
|
|
|
|
func reg(t *testing.T, s *Store, d Desc) Key {
|
|
t.Helper()
|
|
k, err := s.Register(d)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return k
|
|
}
|
|
|
|
func volume(owner string) Desc {
|
|
return Desc{ID: owner + ".music", Owner: owner, Kind: schema.KindFloat64, Default: schema.Float64(1), Flags: User | Project, Min: 0, Max: 1, HasMin: true, HasMax: true}
|
|
}
|
|
|
|
func TestOwnerPrefix(t *testing.T) {
|
|
if _, err := New().Register(Desc{ID: "other.x", Owner: "audio", Kind: schema.KindBool}); err == nil {
|
|
t.Fatal("a setting outside its owner was accepted")
|
|
}
|
|
}
|
|
|
|
func TestLayers(t *testing.T) {
|
|
s := New()
|
|
k := reg(t, s, volume("audio"))
|
|
s.Freeze()
|
|
s.LoadProject(&aetext.Node{Head: ProjectHead, Blocks: []*aetext.Node{{Head: "audio", Fields: []aetext.Field{{Name: "music", Value: aetext.Float64(0.8)}}}}}, "project")
|
|
if s.Float(k) != 0.8 || s.Source(k) != LayerProject {
|
|
t.Fatalf("project layer: %v %v", s.Float(k), s.Source(k))
|
|
}
|
|
s.Start()
|
|
tx := s.Begin()
|
|
if err := tx.Set(k, schema.Float64(0.5)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := tx.Apply(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.Override(k, schema.Float64(0.1)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if s.Float(k) != 0.1 || s.Source(k) != LayerSession {
|
|
t.Fatal("session layer does not win")
|
|
}
|
|
if strings.Contains(string(s.EncodeProfile()), "0.1") {
|
|
t.Fatal("a session override reached the profile")
|
|
}
|
|
_ = s.ClearOverride(k)
|
|
if s.Float(k) != 0.5 {
|
|
t.Fatal("clearing the override did not return the user value")
|
|
}
|
|
}
|
|
|
|
func TestRollback(t *testing.T) {
|
|
s := New()
|
|
var got []float64
|
|
a := volume("audio")
|
|
a.Apply = func(v schema.Value) error {
|
|
got = append(got, v.F)
|
|
return nil
|
|
}
|
|
b := Desc{ID: "display.mode", Owner: "display", Kind: schema.KindInt64, Flags: User, Apply: func(v schema.Value) error {
|
|
if v.I == 1 {
|
|
return errors.New("refused")
|
|
}
|
|
return nil
|
|
}}
|
|
ka, kb := reg(t, s, a), reg(t, s, b)
|
|
s.Freeze()
|
|
s.Start()
|
|
got = nil
|
|
tx := s.Begin()
|
|
_ = tx.Set(ka, schema.Float64(0.3))
|
|
_ = tx.Set(kb, schema.Int64(1))
|
|
if tx.Apply() == nil {
|
|
t.Fatal("a failing apply was accepted")
|
|
}
|
|
if s.Float(ka) != 1 || s.Int(kb) != 0 {
|
|
t.Fatal("the transaction was not rolled back")
|
|
}
|
|
if len(got) != 2 || got[1] != 1 {
|
|
t.Fatalf("the first setting was not re-applied with its old value: %v", got)
|
|
}
|
|
}
|
|
|
|
func TestConfirm(t *testing.T) {
|
|
s := New()
|
|
k := reg(t, s, volume("audio"))
|
|
s.Freeze()
|
|
s.Start()
|
|
s.Tick(10)
|
|
tx := s.Begin()
|
|
_ = tx.Set(k, schema.Float64(0.2))
|
|
if err := tx.ApplyWithConfirm(DefaultConfirm); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s.Tick(20)
|
|
if s.Float(k) != 0.2 {
|
|
t.Fatal("reverted too early")
|
|
}
|
|
s.Tick(26)
|
|
if s.Float(k) != 1 {
|
|
t.Fatal("not reverted without confirmation")
|
|
}
|
|
}
|
|
|
|
func TestSupportsAndDependencies(t *testing.T) {
|
|
s := New()
|
|
mode := reg(t, s, Desc{ID: "display.mode", Owner: "display", Kind: schema.KindInt64, Flags: User})
|
|
res := reg(t, s, Desc{ID: "display.res", Owner: "display", Kind: schema.KindString, Flags: User,
|
|
Supports: func(v schema.Value) bool { return v.S != "8000x8000" },
|
|
EnabledWhen: func(s *Store) bool { return s.Int(mode) != 1 },
|
|
ChoicesWhen: func(s *Store) []Choice {
|
|
if s.Int(mode) == 2 {
|
|
return []Choice{{Name: "a", Value: schema.String("1920x1080")}}
|
|
}
|
|
return nil
|
|
}})
|
|
s.Freeze()
|
|
s.Start()
|
|
if s.Begin().Set(res, schema.String("8000x8000")) == nil {
|
|
t.Fatal("the platform validator did not reject")
|
|
}
|
|
_ = s.Override(mode, schema.Int64(1))
|
|
if s.Enabled(res) {
|
|
t.Fatal("EnabledWhen ignored")
|
|
}
|
|
_ = s.Override(mode, schema.Int64(2))
|
|
if s.Begin().Set(res, schema.String("1280x720")) == nil {
|
|
t.Fatal("ChoicesWhen ignored")
|
|
}
|
|
}
|
|
|
|
func TestProfile(t *testing.T) {
|
|
s := New()
|
|
k := reg(t, s, volume("audio"))
|
|
_ = s.OwnerVersion(OwnerVersion{Owner: "audio", Version: 2, Migrate: func(n *aetext.Node, from, to int) error {
|
|
for i := range n.Fields {
|
|
if n.Fields[i].Name == "bgm" {
|
|
n.Fields[i].Name = "music"
|
|
}
|
|
}
|
|
return nil
|
|
}})
|
|
s.Freeze()
|
|
src := "profile version=1 {\n audio {\n bgm=0.25\n future=1\n }\n modded {\n x=1\n }\n}\n"
|
|
if err := s.LoadProfile([]byte(src), "p"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if s.Float(k) != 0.25 {
|
|
t.Fatal("migration was not applied")
|
|
}
|
|
out := string(s.EncodeProfile())
|
|
for _, want := range []string{"audio version=2", "music=0.25", "future=1", "modded"} {
|
|
if !strings.Contains(out, want) {
|
|
t.Fatalf("profile lost %q:\n%s", want, out)
|
|
}
|
|
}
|
|
b := New()
|
|
reg(t, b, volume("audio"))
|
|
b.Freeze()
|
|
if b.LoadProfile([]byte("profile {"), "broken") == nil || len(b.Diagnostics()) == 0 {
|
|
t.Fatal("a damaged profile gave no diagnostic")
|
|
}
|
|
}
|
|
|
|
func TestNotifyOnceAndPreset(t *testing.T) {
|
|
s := New()
|
|
p := reg(t, s, Desc{ID: "display.preset", Owner: "display", Kind: schema.KindEnum, Flags: User, Preset: true, Default: schema.Enum(1),
|
|
Choices: []Choice{{Name: "Low", Value: schema.Enum(0)}, {Name: "High", Value: schema.Enum(1)}, {Name: PresetCustom, Value: schema.Enum(2)}}})
|
|
q := reg(t, s, Desc{ID: "display.quality", Owner: "display", Kind: schema.KindInt64, Flags: User, Default: schema.Int64(3),
|
|
Presets: map[string]schema.Value{"Low": schema.Int64(1), "High": schema.Int64(3)}})
|
|
s.Freeze()
|
|
s.Start()
|
|
calls := 0
|
|
s.Subscribe(q, func(Key) { calls++ })
|
|
tx := s.Begin()
|
|
_ = tx.Set(p, schema.Enum(0))
|
|
_ = tx.Apply()
|
|
if s.Int(q) != 1 || calls != 1 {
|
|
t.Fatalf("preset: quality %d, calls %d", s.Int(q), calls)
|
|
}
|
|
tx = s.Begin()
|
|
_ = tx.Set(q, schema.Int64(2))
|
|
_ = tx.Set(q, schema.Int64(2))
|
|
_ = tx.Apply()
|
|
if s.ChoiceName(p) != PresetCustom || calls != 2 {
|
|
t.Fatalf("manual change: preset %s, calls %d", s.ChoiceName(p), calls)
|
|
}
|
|
}
|
|
|
|
func TestReadAllocs(t *testing.T) {
|
|
s := New()
|
|
k := reg(t, s, volume("audio"))
|
|
s.Freeze()
|
|
if n := testing.AllocsPerRun(100, func() { _ = s.Float(k) }); n != 0 {
|
|
t.Fatalf("reading a setting allocates %v", n)
|
|
}
|
|
}
|