- commit
- 58c408d991d88bf4dc958be282e713a95763026b
- parent
- 0e2be937b131e0213f24fdea652e0c29dc54f0f9
- author
- Dami <dami.etoile@gmail.com>
| M | architecture_test.go | 64 | ++++++++-------- |
| M | registry/ | 121 | ++++++++++++++++++++++++++++++- |
@@ -5,6 +5,8 @@ import (
55 "go/token"66 "os"77 "path/filepath"8+ "runtime"9+ "slices"810 "strings"911 "testing"1012 )@@ -43,41 +45,11 @@ func TestGamePackageDoesNotImportPDFExport(t *testing.T) {
4345 }4446 4547 func TestCatalogPackageDoesNotImportConcreteGames(t *testing.T) {46- assertPackageDoesNotImport(t, "catalog", []string{47- "github.com/FelineStateMachine/puzzletea/fillomino",48- "github.com/FelineStateMachine/puzzletea/hashiwokakero",49- "github.com/FelineStateMachine/puzzletea/hitori",50- "github.com/FelineStateMachine/puzzletea/lightsout",51- "github.com/FelineStateMachine/puzzletea/nonogram",52- "github.com/FelineStateMachine/puzzletea/nurikabe",53- "github.com/FelineStateMachine/puzzletea/rippleeffect",54- "github.com/FelineStateMachine/puzzletea/shikaku",55- "github.com/FelineStateMachine/puzzletea/spellpuzzle",56- "github.com/FelineStateMachine/puzzletea/sudoku",57- "github.com/FelineStateMachine/puzzletea/sudokurgb",58- "github.com/FelineStateMachine/puzzletea/takuzu",59- "github.com/FelineStateMachine/puzzletea/takuzuplus",60- "github.com/FelineStateMachine/puzzletea/wordsearch",61- })48+ assertPackageDoesNotImport(t, "catalog", concreteGameImportPaths(t))6249 }6350 6451 func TestBuiltinPrintDoesNotImportConcreteGames(t *testing.T) {65- assertPackageDoesNotImport(t, "builtinprint", []string{66- "github.com/FelineStateMachine/puzzletea/fillomino",67- "github.com/FelineStateMachine/puzzletea/hashiwokakero",68- "github.com/FelineStateMachine/puzzletea/hitori",69- "github.com/FelineStateMachine/puzzletea/lightsout",70- "github.com/FelineStateMachine/puzzletea/nonogram",71- "github.com/FelineStateMachine/puzzletea/nurikabe",72- "github.com/FelineStateMachine/puzzletea/rippleeffect",73- "github.com/FelineStateMachine/puzzletea/shikaku",74- "github.com/FelineStateMachine/puzzletea/spellpuzzle",75- "github.com/FelineStateMachine/puzzletea/sudoku",76- "github.com/FelineStateMachine/puzzletea/sudokurgb",77- "github.com/FelineStateMachine/puzzletea/takuzu",78- "github.com/FelineStateMachine/puzzletea/takuzuplus",79- "github.com/FelineStateMachine/puzzletea/wordsearch",80- })52+ assertPackageDoesNotImport(t, "builtinprint", concreteGameImportPaths(t))8153 }8254 8355 func TestSessionPackageDoesNotUseNameDerivedRunMetadata(t *testing.T) {@@ -143,6 +115,34 @@ func assertPackageDoesNotImport(t *testing.T, dir string, forbidden []string) {
143115 }144116 }145117 118+func concreteGameImportPaths(t testing.TB) []string {119+ t.Helper()120+121+ pattern := filepath.Join(repoRoot(t), "*", "gamemode.go")122+ matches, err := filepath.Glob(pattern)123+ if err != nil {124+ t.Fatalf("glob concrete game packages: %v", err)125+ }126+127+ importPaths := make([]string, 0, len(matches))128+ for _, match := range matches {129+ dir := filepath.Base(filepath.Dir(match))130+ importPaths = append(importPaths, "github.com/FelineStateMachine/puzzletea/"+dir)131+ }132+ slices.Sort(importPaths)133+ return importPaths134+}135+136+func repoRoot(t testing.TB) string {137+ t.Helper()138+139+ _, path, _, ok := runtime.Caller(0)140+ if !ok {141+ t.Fatal("runtime.Caller failed")142+ }143+ return filepath.Clean(filepath.Dir(path))144+}145+146146 func assertFileDoesNotContain(t *testing.T, path string, forbidden []string) {147147 t.Helper()148148 @@ -1,6 +1,16 @@
11 package registry22 3-import "testing"3+import (4+ "go/ast"5+ "go/parser"6+ "go/token"7+ "path/filepath"8+ "reflect"9+ "runtime"10+ "slices"11+ "strings"12+ "testing"13+)414 515 func TestResolveNormalizesSpacingAndAliases(t *testing.T) {616 tests := []struct {@@ -55,6 +65,14 @@ func TestEntriesStayAlignedWithDefinitions(t *testing.T) {
5565 }5666 }5767 68+func TestRegistryAllMatchesConcreteGamePackages(t *testing.T) {69+ got := registryAllImportPaths(t)70+ want := concreteGameImportPaths(t)71+ if !reflect.DeepEqual(got, want) {72+ t.Fatalf("registry all import paths = %v, want %v", got, want)73+ }74+}75+5876 func TestPrintAdaptersStayAlignedWithRegistryEntries(t *testing.T) {5977 for _, entry := range Entries() {6078 if entry.Definition.Name == "Lights Out" {@@ -68,3 +86,104 @@ func TestPrintAdaptersStayAlignedWithRegistryEntries(t *testing.T) {
6886 }6987 }7088 }89+90+func registryAllImportPaths(t *testing.T) []string {91+ t.Helper()92+93+ fset := token.NewFileSet()94+ file, err := parser.ParseFile(fset, "registry.go", nil, 0)95+ if err != nil {96+ t.Fatalf("parse registry.go: %v", err)97+ }98+99+ imports := make(map[string]string)100+ for _, imp := range file.Imports {101+ path := strings.Trim(imp.Path.Value, `"`)102+ name := importName(path, imp.Name)103+ imports[name] = path104+ }105+106+ paths := registryAllSelectorImportPaths(t, file, imports)107+ slices.Sort(paths)108+ return paths109+}110+111+func registryAllSelectorImportPaths(t *testing.T, file *ast.File, imports map[string]string) []string {112+ t.Helper()113+114+ for _, decl := range file.Decls {115+ genDecl, ok := decl.(*ast.GenDecl)116+ if !ok || genDecl.Tok != token.VAR {117+ continue118+ }119+ for _, spec := range genDecl.Specs {120+ valueSpec, ok := spec.(*ast.ValueSpec)121+ if !ok || len(valueSpec.Names) != 1 || valueSpec.Names[0].Name != "all" || len(valueSpec.Values) != 1 {122+ continue123+ }124+125+ lit, ok := valueSpec.Values[0].(*ast.CompositeLit)126+ if !ok {127+ t.Fatalf("registry all value is %T, want composite literal", valueSpec.Values[0])128+ }129+130+ paths := make([]string, 0, len(lit.Elts))131+ for _, elt := range lit.Elts {132+ selector, ok := elt.(*ast.SelectorExpr)133+ if !ok {134+ t.Fatalf("registry all element is %T, want selector", elt)135+ }136+ pkg, ok := selector.X.(*ast.Ident)137+ if !ok {138+ t.Fatalf("registry all selector receiver is %T, want identifier", selector.X)139+ }140+141+ path, ok := imports[pkg.Name]142+ if !ok {143+ t.Fatalf("registry all package %q missing from imports", pkg.Name)144+ }145+ paths = append(paths, path)146+ }147+ return paths148+ }149+ }150+151+ t.Fatal("registry all declaration not found")152+ return nil153+}154+155+func importName(path string, name *ast.Ident) string {156+ if name != nil {157+ return name.Name158+ }159+ parts := strings.Split(path, "/")160+ return parts[len(parts)-1]161+}162+163+func concreteGameImportPaths(t testing.TB) []string {164+ t.Helper()165+166+ pattern := filepath.Join(repoRoot(t), "*", "gamemode.go")167+ matches, err := filepath.Glob(pattern)168+ if err != nil {169+ t.Fatalf("glob concrete game packages: %v", err)170+ }171+172+ importPaths := make([]string, 0, len(matches))173+ for _, match := range matches {174+ dir := filepath.Base(filepath.Dir(match))175+ importPaths = append(importPaths, "github.com/FelineStateMachine/puzzletea/"+dir)176+ }177+ slices.Sort(importPaths)178+ return importPaths179+}180+181+func repoRoot(t testing.TB) string {182+ t.Helper()183+184+ _, path, _, ok := runtime.Caller(0)185+ if !ok {186+ t.Fatal("runtime.Caller failed")187+ }188+ return filepath.Clean(filepath.Join(filepath.Dir(path), ".."))189+}