package main import ( "os" "path/filepath" "testing" ) // each test runs in its own directory: totals.json and offset.txt resolve // relative to the executable, so we point that at the temp dir. func fresh(t *testing.T, legacy string) { t.Helper() dir := t.TempDir() testExeDir = dir if legacy != "" { if err := os.WriteFile(filepath.Join(dir, "offset.txt"), []byte(legacy), 0644); err != nil { t.Fatal(err) } } totals = newTotalsStore() st = state{} st.init(totals) } func total(t *testing.T) int64 { t.Helper(); return st.snapshot().Total } // THE EMMA CASE: switching characters has to move the number RIGHT AWAY, // without waiting for anyone to die. func TestCharacterSwitchUpdatesInstantly(t *testing.T) { fresh(t, "") st.setCharacter(0, "emma", 40) st.setReading(40, false) if got := total(t); got != 40 { t.Fatalf("emma should start at her 40 deaths, got %d", got) } // goes to the menu and loads another character, with its own count st.setPlayerUnloaded("menu") st.setCharacter(1, "emmatherock", 7) if got := total(t); got != 7 { t.Fatalf("loading emmatherock should jump the total to 7 WITHOUT dying, got %d", got) } st.setReading(7, false) if got := total(t); got != 7 { t.Fatalf("after the reading it should still be 7, got %d", got) } // and switching back shows what the game tracks for emma st.setPlayerUnloaded("menu") st.setCharacter(0, "emma", 41) st.setReading(41, false) if got := total(t); got != 41 { t.Fatalf("switching back to emma should give 41, got %d", got) } } // Two characters with the SAME name must not get mixed up. func TestHomonymsDontMix(t *testing.T) { fresh(t, "") st.setCharacter(0, "Tarnished", 100) st.setReading(100, false) st.setCharacter(3, "Tarnished", 5) st.setReading(5, false) if got := total(t); got != 5 { t.Fatalf("slot 3's should give 5, got %d", got) } if c, ok := totals.get("slot0"); !ok || c.Total != 100 { t.Fatalf("slot 0's should still be 100, got %+v", c) } } // The total is the game's, no matter if a stale offset.txt is lying // around next to the program. func TestIgnoresStaleOffset(t *testing.T) { fresh(t, "124") st.setCharacter(0, "emma", 30) st.setReading(30, false) if got := total(t); got != 30 { t.Fatalf("should show the game's 30, not the old file's 124, got %d", got) } } // Without being able to read the slot, it falls back to the name and // keeps working. func TestNoSlotFallsBackToName(t *testing.T) { fresh(t, "") st.setCharacter(-1, "emma", 40) st.setReading(40, false) if got := total(t); got != 40 { t.Fatalf("should give 40, got %d", got) } if _, ok := totals.get("name:emma"); !ok { t.Fatal("should have saved it by name") } } // Reopening the program with the game closed shows the last known // character instead of a zero. func TestRemembersTheLastOneOnReopen(t *testing.T) { fresh(t, "") st.setCharacter(2, "emma", 88) st.setReading(88, false) totals = newTotalsStore() // simulates reopening the program st = state{} st.init(totals) if got := total(t); got != 88 { t.Fatalf("reopening should show 88, got %d", got) } if st.snapshot().CharName != "emma" { t.Fatalf("and remember the name, got %q", st.snapshot().CharName) } }