package engine import ( "fmt" "os" "path/filepath" "runtime" "testing" "scrabble-solver/rules" "scrabble-solver/scrabble" ) // testVersion labels the single dictionary version the tests register. const testVersion = "test" // testReg is the shared registry of all three variants, hydrated once by // TestMain and reused by the read-only tests. var testReg *Registry // TestMain loads the committed dictionaries once and shares them with every // test. It fails loudly when the dictionary directory is absent (per // docs/TESTING.md) rather than skipping coverage. func TestMain(m *testing.M) { reg, err := Open(testDictDir(), testVersion) if err != nil { fmt.Fprintln(os.Stderr, "engine test setup:", err) os.Exit(1) } testReg = reg code := m.Run() _ = reg.Close() os.Exit(code) } // testDictDir resolves the directory holding the committed scrabble-solver // DAWGs: BACKEND_DICT_DIR when set (used in CI), otherwise the sibling checkout // located relative to this test file. func testDictDir() string { if dir := os.Getenv("BACKEND_DICT_DIR"); dir != "" { return dir } _, file, _, _ := runtime.Caller(0) return filepath.Join(filepath.Dir(file), "..", "..", "..", "..", "scrabble-solver", "dawg") } // centre returns the centre square coordinates of rs. func centre(rs *rules.Ruleset) (row, col int) { return rs.Center / rs.Cols, rs.Center % rs.Cols } // placementsForWord lays word out from (row, col) along dir, resolving each rune // through the ruleset's alphabet. It expresses no blanks. func placementsForWord(t *testing.T, rs *rules.Ruleset, row, col int, dir scrabble.Direction, word string) []scrabble.Placement { t.Helper() var ps []scrabble.Placement for i, r := range []rune(word) { idx, err := rs.Alphabet.Index(string(r)) if err != nil { t.Fatalf("index %q: %v", string(r), err) } rr, cc := row, col if dir == scrabble.Horizontal { cc += i } else { rr += i } ps = append(ps, scrabble.Placement{Row: rr, Col: cc, Letter: idx}) } return ps }