package fejkdata import ( "bytes" "fmt" "go/format" "io/fs" "os" "reflect" "slices" "sort" "sync" "testing" ) const shippedIndexFile = "shippedindex.go" // newShippedWhole loads every shipped category in New, as a --data-path load does, so a test // reads the whole shipped tree. func newShippedWhole(t testing.TB, opts ...Option) *Generator { t.Helper() data, err := fs.Sub(shippedFS, "data") if err != nil { t.Fatal(err) } f, err := New(append([]Option{WithoutShippedData(), WithDataFS(data)}, opts...)...) if err != nil { t.Fatalf("New() = %v", err) } return f } // loadedCategories is the dot path of every category f has parsed and linked. func loadedCategories(f *Generator) []string { var out []string for _, s := range categorySites(&f.root) { out = append(out, s.path) } return out } // REGENERATE=1, which go generate sets, rewrites the index. func TestShippedIndexIsCurrent(t *testing.T) { got, err := shippedIndexSource(newShippedWhole(t)) if err != nil { t.Fatal(err) } if os.Getenv("REGENERATE") == "1" { if err := os.WriteFile(shippedIndexFile, got, 0o644); err != nil { t.Fatal(err) } return } file, err := os.ReadFile(shippedIndexFile) if err != nil { t.Fatal(err) } want, err := format.Source(file) // docs/decisions.md#the-format-check-runs-on-the-latest-go-only if err != nil { t.Fatal(err) } if string(got) != string(want) { t.Fatalf("%s is stale for data/; regenerate it with go generate", shippedIndexFile) } } func shippedIndexSource(whole *Generator) ([]byte, error) { var b bytes.Buffer b.WriteString("// Code generated by TestShippedIndexIsCurrent with REPIN=1. DO NOT EDIT.\n\npackage fejkdata\n\nvar shippedIndex = map[string]shippedEntry{\n") for _, s := range categorySites(&whole.root) { fmt.Fprintf(&b, "%q: {", s.path) if t, isTable := s.n.(*table); isTable && t.parentIndex >= 0 { fmt.Fprintf(&b, "parent: %q, ", t.header[t.parentIndex]) } ps := paths(s.n) sort.Strings(ps) fmt.Fprintf(&b, "paths: %#v},\n", ps) } b.WriteString("}\n") return format.Source(b.Bytes()) } func TestNewLoadsNoShippedCategory(t *testing.T) { f, err := New() if err != nil { t.Fatalf("New() = %v", err) } if got := loadedCategories(f); len(got) > 0 { t.Fatalf("New() loaded %v", got) } } func TestReachingACategoryLoadsWhatItReads(t *testing.T) { for _, tc := range []struct { reach func(f *Generator) error want, absent []string }{ { reach: func(f *Generator) error { _, err := f.Fake("sv_SE.person.first"); return err }, want: []string{"sv_SE.first-name", "sv_SE.last-name", "sv_SE.person", "sv_SE.sex", "sv_SE.title"}, }, { reach: func(f *Generator) error { _, err := f.Fake("geo.SE.region"); return err }, want: []string{"geo.SE.locality", "geo.SE.municipality", "geo.SE.postal-code", "geo.SE.region", "geo.SE.street"}, absent: []string{"geo.SE.address", "geo.US.region"}, }, { reach: func(f *Generator) error { _, err := f.FakeTemplate("{/misc.territory[SE].name}"); return err }, want: []string{"misc.territory", "misc.timezone"}, absent: []string{"misc.tld"}, }, { reach: func(f *Generator) error { _, err := f.FakeRecord("misc.uuid"); return err }, want: []string{"misc.uuid"}, absent: []string{"misc.objectid"}, }, { reach: func(f *Generator) error { var v struct { Name string `fake:"sv_SE.first-name"` } return f.FakeStruct(&v) }, want: []string{"sv_SE.first-name"}, absent: []string{"sv_SE.person"}, }, } { f, err := New() if err != nil { t.Fatalf("New() = %v", err) } if err := tc.reach(f); err != nil { t.Fatal(err) } got := loadedCategories(f) for _, w := range tc.want { if !slices.Contains(got, w) { t.Errorf("loaded %v, want %s among them", got, w) } } for _, a := range tc.absent { if slices.Contains(got, a) { t.Errorf("loaded %v, want %s left unloaded", got, a) } } } } func TestADataPathLoadsTheShippedSetWhole(t *testing.T) { f, err := New(WithDataPath(writeData(t, map[string]string{"x": `"y"`}))) if err != nil { t.Fatalf("New() = %v", err) } if got, want := loadedCategories(f), loadedCategories(newShippedWhole(t, WithDataPath(writeData(t, map[string]string{"x": `"y"`})))); !reflect.DeepEqual(got, want) { t.Fatalf("loaded %v, want %v", got, want) } } func TestOnDemandRendersAsTheWholeLoad(t *testing.T) { whole := newShippedWhole(t) paths := whole.List() lazy, err := New() if err != nil { t.Fatalf("New() = %v", err) } if got := lazy.List(); !reflect.DeepEqual(got, paths) { t.Fatalf("List() on demand = %v, want %v", got, paths) } a, err := New(WithSeed(7)) if err != nil { t.Fatal(err) } b := newShippedWhole(t, WithSeed(7)) for _, p := range paths { for range 3 { got, gotErr := a.Fake(p) want, wantErr := b.Fake(p) if got != want || (gotErr == nil) != (wantErr == nil) { t.Fatalf("Fake(%q) on demand = %q, %v; whole = %q, %v", p, got, gotErr, want, wantErr) } } } } func TestEveryShippedCategoryLoadsAlone(t *testing.T) { for p := range shippedIndex { f, err := New() if err != nil { t.Fatalf("New() = %v", err) } if _, err := f.Fake(p); err != nil { t.Errorf("Fake(%q) on a fresh New() = %v", p, err) } } } func TestListRunsBesideAnOnDemandLoad(t *testing.T) { f, err := New() if err != nil { t.Fatalf("New() = %v", err) } var wg sync.WaitGroup for _, p := range []string{"sv_SE.person", "geo.US.address", "misc.territory"} { wg.Add(2) go func() { defer wg.Done() if _, err := f.Fake(p); err != nil { t.Error(err) } }() go func() { defer wg.Done() f.List() }() } wg.Wait() }