148 lines
3.5 KiB
Go
148 lines
3.5 KiB
Go
package emotion
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
func mustMachine(t *testing.T, s EmotionState) *Machine {
|
|
t.Helper()
|
|
m, err := NewMachine(Params{InitialState: s})
|
|
if err != nil {
|
|
t.Fatalf("NewMachine(%q) error: %v", s, err)
|
|
}
|
|
return m
|
|
}
|
|
|
|
func TestManager_EmotionFollowsPersona(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mg := NewManager()
|
|
a, err := NewAffection(50, 0, 100)
|
|
if err != nil {
|
|
t.Fatalf("NewAffection() error: %v", err)
|
|
}
|
|
mg.Set("personaA", mustMachine(t, Excited), a)
|
|
mg.Set("personaB", mustMachine(t, Calm), nil)
|
|
|
|
// Transition personaA; personaB must be untouched.
|
|
if err := mg.Transition("personaA", Happy); err != nil {
|
|
t.Fatalf("Transition(personaA, happy) error: %v", err)
|
|
}
|
|
mA, _, ok := mg.Get("personaA")
|
|
if !ok {
|
|
t.Fatal("personaA not found")
|
|
}
|
|
if mA.Current() != Happy {
|
|
t.Fatalf("personaA Current() = %q, want %q", mA.Current(), Happy)
|
|
}
|
|
mB, _, _ := mg.Get("personaB")
|
|
if mB.Current() != Calm {
|
|
t.Fatalf("personaB Current() = %q, want %q (isolation)", mB.Current(), Calm)
|
|
}
|
|
}
|
|
|
|
func TestManager_AdjustAffectionIsolated(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mg := NewManager()
|
|
affA, err := NewAffection(40, 0, 100)
|
|
if err != nil {
|
|
t.Fatalf("NewAffection() error: %v", err)
|
|
}
|
|
affB, err := NewAffection(10, 0, 100)
|
|
if err != nil {
|
|
t.Fatalf("NewAffection() error: %v", err)
|
|
}
|
|
mg.Set("personaA", mustMachine(t, Calm), affA)
|
|
mg.Set("personaB", mustMachine(t, Calm), affB)
|
|
|
|
before, after, err := mg.AdjustAffection("personaA", 30)
|
|
if err != nil {
|
|
t.Fatalf("AdjustAffection(personaA) error: %v", err)
|
|
}
|
|
if before != 40 || after != 70 {
|
|
t.Fatalf("AdjustAffection(personaA) before/after = %d/%d, want 40/70", before, after)
|
|
}
|
|
if got := affB.Value(); got != 10 {
|
|
t.Fatalf("personaB value = %d, want 10 (isolation)", got)
|
|
}
|
|
}
|
|
|
|
func TestManager_TransitionUnknownPersona(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mg := NewManager()
|
|
mg.Set("personaA", mustMachine(t, Calm), nil)
|
|
err := mg.Transition("ghost", Happy)
|
|
if !errors.Is(err, ErrUnknownPersona) {
|
|
t.Fatalf("Transition(ghost) error = %v, want ErrUnknownPersona", err)
|
|
}
|
|
if _, _, err := mg.AdjustAffection("ghost", 1); !errors.Is(err, ErrUnknownPersona) {
|
|
t.Fatalf("AdjustAffection(ghost) error = %v, want ErrUnknownPersona", err)
|
|
}
|
|
}
|
|
|
|
func TestManager_SetGetRemoveCount(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mg := NewManager()
|
|
mg.Set("a", mustMachine(t, Calm), nil)
|
|
mg.Set("b", mustMachine(t, Happy), nil)
|
|
if got := mg.Count(); got != 2 {
|
|
t.Fatalf("Count() = %d, want 2", got)
|
|
}
|
|
if _, _, ok := mg.Get("a"); !ok {
|
|
t.Fatal("Get(a) not found")
|
|
}
|
|
if !mg.Remove("a") {
|
|
t.Fatal("Remove(a) should report true")
|
|
}
|
|
if mg.Remove("a") {
|
|
t.Fatal("Remove(a) again should report false")
|
|
}
|
|
if got := mg.Count(); got != 1 {
|
|
t.Fatalf("Count() after remove = %d, want 1", got)
|
|
}
|
|
}
|
|
|
|
func TestManager_ConcurrentAccess(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mg := NewManager()
|
|
const n = 8
|
|
for i := 0; i < n; i++ {
|
|
id := idOf(i)
|
|
aff, _ := NewAffection(50, 0, 100)
|
|
mg.Set(id, mustMachine(t, Excited), aff)
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < n; i++ {
|
|
id := idOf(i)
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
_, _, _ = mg.Get(id)
|
|
_, _, _ = mg.AdjustAffection(id, 5)
|
|
_ = mg.Transition(id, Happy)
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
|
|
for i := 0; i < n; i++ {
|
|
id := idOf(i)
|
|
m, _, ok := mg.Get(id)
|
|
if !ok {
|
|
t.Fatalf("persona %s missing after concurrent access", id)
|
|
}
|
|
if m.Current() != Happy {
|
|
t.Fatalf("persona %s Current() = %q, want %q", id, m.Current(), Happy)
|
|
}
|
|
}
|
|
}
|
|
|
|
func idOf(i int) string { return fmt.Sprintf("persona%d", i) }
|