Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33
+19
-19
@@ -6,17 +6,17 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func benchGenerator(b *testing.B, dir string) *Generator {
|
||||
func benchGenerator(b *testing.B, data Option) *Generator {
|
||||
b.Helper()
|
||||
f, err := New(WithoutShippedData(), WithDataPath(dir), WithSeed(1))
|
||||
f, err := New(data, WithSeed(1))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func benchPath(b *testing.B, dir, path string) {
|
||||
f := benchGenerator(b, dir)
|
||||
func benchPath(b *testing.B, data Option, path string) {
|
||||
f := benchGenerator(b, data)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -26,12 +26,12 @@ func benchPath(b *testing.B, dir, path string) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPerson(b *testing.B) { benchPath(b, "data", "sv_SE.person") }
|
||||
func BenchmarkAddress(b *testing.B) { benchPath(b, "data", "sv_SE.address") }
|
||||
func BenchmarkWord(b *testing.B) { benchPath(b, "data", "sv_SE.word") }
|
||||
func BenchmarkCreditcard(b *testing.B) { benchPath(b, "data", "misc.creditcard") }
|
||||
func BenchmarkPersonnummer(b *testing.B) { benchPath(b, "data", "sv_SE.personnummer") }
|
||||
func BenchmarkUUIDv7(b *testing.B) { benchPath(b, "data", "misc.uuid") }
|
||||
func BenchmarkPerson(b *testing.B) { benchPath(b, withShipped(), "sv_SE.person") }
|
||||
func BenchmarkAddress(b *testing.B) { benchPath(b, withShipped(), "sv_SE.address") }
|
||||
func BenchmarkWord(b *testing.B) { benchPath(b, withShipped(), "sv_SE.word") }
|
||||
func BenchmarkCreditcard(b *testing.B) { benchPath(b, withShipped(), "misc.creditcard") }
|
||||
func BenchmarkPersonnummer(b *testing.B) { benchPath(b, withShipped(), "sv_SE.personnummer") }
|
||||
func BenchmarkUUIDv7(b *testing.B) { benchPath(b, withShipped(), "misc.uuid") }
|
||||
|
||||
func tmpData(b *testing.B, name, body string) string {
|
||||
b.Helper()
|
||||
@@ -44,17 +44,17 @@ func tmpData(b *testing.B, name, body string) string {
|
||||
|
||||
func BenchmarkCalc(b *testing.B) {
|
||||
dir := tmpData(b, "inv", `{"format":"{net} x {qty} = {calc(net * qty, 2)}","net":"19.99","qty":"3"}`)
|
||||
benchPath(b, dir, "inv")
|
||||
benchPath(b, WithDataPath(dir), "inv")
|
||||
}
|
||||
|
||||
func BenchmarkLongLiteral(b *testing.B) {
|
||||
dir := tmpData(b, "sql", `{"format":"INSERT INTO customers (id, name, city) VALUES (123, '{word}', '{word}');","word":["alpha","beta","gamma","delta"]}`)
|
||||
benchPath(b, dir, "sql")
|
||||
benchPath(b, WithDataPath(dir), "sql")
|
||||
}
|
||||
|
||||
func BenchmarkRepeat(b *testing.B) {
|
||||
dir := tmpData(b, "many", `{"format":"{word}","repeat":20,"separator":", ","word":["alpha","beta","gamma","delta"]}`)
|
||||
benchPath(b, dir, "many")
|
||||
benchPath(b, WithDataPath(dir), "many")
|
||||
}
|
||||
|
||||
// BenchmarkNamed measures a format that binds a field to a name and reads two paths through
|
||||
@@ -62,18 +62,18 @@ func BenchmarkRepeat(b *testing.B) {
|
||||
// independent fields.
|
||||
func BenchmarkNamed(b *testing.B) {
|
||||
dir := tmpData(b, "addr", `{"format":"{place as p}{p.postal-code} {p.locality}","place":[{"format":"{locality}","locality":"Stockholm","postal-code":"1{digits(2)} {digits(2)}"},{"format":"{locality}","locality":"Tranås","postal-code":"573 {digits(2)}"}]}`)
|
||||
benchPath(b, dir, "addr")
|
||||
benchPath(b, WithDataPath(dir), "addr")
|
||||
}
|
||||
|
||||
func BenchmarkUnnamed(b *testing.B) {
|
||||
dir := tmpData(b, "addr", `{"format":"{postal-code} {locality}","postal-code":["1{digits(2)} {digits(2)}","573 {digits(2)}"],"locality":["Stockholm","Tranås"]}`)
|
||||
benchPath(b, dir, "addr")
|
||||
benchPath(b, WithDataPath(dir), "addr")
|
||||
}
|
||||
|
||||
// BenchmarkNamedDeep reads two paths through a name and an intermediate level.
|
||||
func BenchmarkNamedDeep(b *testing.B) {
|
||||
dir := tmpData(b, "addr", `{"format":"{p as q}{q.addr.city} {q.addr.zip}","p":{"format":"{addr}","addr":{"format":"{city}","city":["Stockholm","Tranås"],"zip":"1{digits(2)} {digits(2)}"}}}`)
|
||||
benchPath(b, dir, "addr")
|
||||
benchPath(b, WithDataPath(dir), "addr")
|
||||
}
|
||||
|
||||
// BenchmarkNamedWide reads ten paths through one name: the point where the maps a pick keeps
|
||||
@@ -82,13 +82,13 @@ func BenchmarkNamedWide(b *testing.B) {
|
||||
dir := tmpData(b, "row", `{"format":"{r as s}{s.a}{s.b}{s.c}{s.d}{s.e}{s.f}{s.g}{s.h}{s.i}{s.j}","r":[
|
||||
{"format":"x","a":"1","b":"2","c":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"0"},
|
||||
{"format":"y","a":"A","b":"B","c":"C","d":"D","e":"E","f":"F","g":"G","h":"H","i":"I","j":"J"}]}`)
|
||||
benchPath(b, dir, "row")
|
||||
benchPath(b, WithDataPath(dir), "row")
|
||||
}
|
||||
|
||||
func BenchmarkFirstFake(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
f, err := New(WithSeed(1))
|
||||
f, err := New(withShipped(), WithSeed(1))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
@@ -101,7 +101,7 @@ func BenchmarkFirstFake(b *testing.B) {
|
||||
func BenchmarkNew(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := New(WithSeed(1)); err != nil {
|
||||
if _, err := New(withShipped(), WithSeed(1)); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -303,14 +303,14 @@ func TestCalcRefusesABadOperand(t *testing.T) {
|
||||
"zero": `"0"`,
|
||||
"card": card,
|
||||
})
|
||||
if _, err := New(WithDataPath(dir), WithSeed(1)); err == nil || !strings.Contains(err.Error(), want) {
|
||||
if _, err := New(withShipped(), WithDataPath(dir), WithSeed(1)); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("New with card %s = %v, want an error containing %q", card, err, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestATypedColumnRefusesACalcOverNoDigits(t *testing.T) {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{
|
||||
"row": `{"format":"","x":{"format":"{calc(d + 1)}","d":"{digits(0)}","datatype":"integer"}}`,
|
||||
})))
|
||||
if err == nil || !strings.Contains(err.Error(), "{digits(0)} prints nothing, which is no number") {
|
||||
@@ -338,7 +338,7 @@ func TestATypedColumnProvesACalcOverAName(t *testing.T) {
|
||||
"word": `["1","x"]`,
|
||||
"order": `{"format":"{/word as w}{w}","double":{"format":"{calc(w * 2)}","datatype":"integer"}}`,
|
||||
})
|
||||
if _, err := New(WithDataPath(bad), WithSeed(1)); err == nil || !strings.Contains(err.Error(), `operand "w": "x" is not a number`) {
|
||||
if _, err := New(withShipped(), WithDataPath(bad), WithSeed(1)); err == nil || !strings.Contains(err.Error(), `operand "w": "x" is not a number`) {
|
||||
t.Fatalf("New = %v, want the typed column refused over an operand that is not always a number", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
svSE = "../../data/sv_SE"
|
||||
enUS = "../../data/en_US"
|
||||
misc = "../../data/misc"
|
||||
svSE = "../../data/sv_SE/sv_SE"
|
||||
enUS = "../../data/en_US/en_US"
|
||||
misc = "../../data/misc/misc"
|
||||
)
|
||||
|
||||
func runOut(stdin string, args ...string) (int, string, string) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package en_US_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"testing"
|
||||
|
||||
"github.com/larvit/fejkdata"
|
||||
"github.com/larvit/fejkdata/data/en_US"
|
||||
)
|
||||
|
||||
func TestFSLoadsByItsManifest(t *testing.T) {
|
||||
if _, err := fs.Stat(en_US.FS, ".fejkdata.json"); err != nil {
|
||||
t.Fatalf("FS holds no manifest: %v", err)
|
||||
}
|
||||
f, err := fejkdata.New(fejkdata.WithDataFS(en_US.FS))
|
||||
if err != nil {
|
||||
t.Fatalf("New(WithDataFS(FS)) = %v", err)
|
||||
}
|
||||
if len(f.List()) == 0 {
|
||||
t.Error("List() is empty, want the module's categories")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package SE_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"testing"
|
||||
|
||||
"github.com/larvit/fejkdata"
|
||||
"github.com/larvit/fejkdata/data/geo/SE"
|
||||
)
|
||||
|
||||
func TestFSLoadsByItsManifest(t *testing.T) {
|
||||
if _, err := fs.Stat(SE.FS, ".fejkdata.json"); err != nil {
|
||||
t.Fatalf("FS holds no manifest: %v", err)
|
||||
}
|
||||
f, err := fejkdata.New(fejkdata.WithDataFS(SE.FS))
|
||||
if err != nil {
|
||||
t.Fatalf("New(WithDataFS(FS)) = %v", err)
|
||||
}
|
||||
if len(f.List()) == 0 {
|
||||
t.Error("List() is empty, want the module's categories")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package US_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"testing"
|
||||
|
||||
"github.com/larvit/fejkdata"
|
||||
"github.com/larvit/fejkdata/data/geo/US"
|
||||
)
|
||||
|
||||
func TestFSLoadsByItsManifest(t *testing.T) {
|
||||
if _, err := fs.Stat(US.FS, ".fejkdata.json"); err != nil {
|
||||
t.Fatalf("FS holds no manifest: %v", err)
|
||||
}
|
||||
f, err := fejkdata.New(fejkdata.WithDataFS(US.FS))
|
||||
if err != nil {
|
||||
t.Fatalf("New(WithDataFS(FS)) = %v", err)
|
||||
}
|
||||
if len(f.List()) == 0 {
|
||||
t.Error("List() is empty, want the module's categories")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package misc_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"testing"
|
||||
|
||||
"github.com/larvit/fejkdata"
|
||||
"github.com/larvit/fejkdata/data/misc"
|
||||
)
|
||||
|
||||
func TestFSLoadsByItsManifest(t *testing.T) {
|
||||
if _, err := fs.Stat(misc.FS, ".fejkdata.json"); err != nil {
|
||||
t.Fatalf("FS holds no manifest: %v", err)
|
||||
}
|
||||
f, err := fejkdata.New(fejkdata.WithDataFS(misc.FS))
|
||||
if err != nil {
|
||||
t.Fatalf("New(WithDataFS(FS)) = %v", err)
|
||||
}
|
||||
if len(f.List()) == 0 {
|
||||
t.Error("List() is empty, want the module's categories")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package sv_SE_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"testing"
|
||||
|
||||
"github.com/larvit/fejkdata"
|
||||
"github.com/larvit/fejkdata/data/sv_SE"
|
||||
)
|
||||
|
||||
func TestFSLoadsByItsManifest(t *testing.T) {
|
||||
if _, err := fs.Stat(sv_SE.FS, ".fejkdata.json"); err != nil {
|
||||
t.Fatalf("FS holds no manifest: %v", err)
|
||||
}
|
||||
f, err := fejkdata.New(fejkdata.WithDataFS(sv_SE.FS))
|
||||
if err != nil {
|
||||
t.Fatalf("New(WithDataFS(FS)) = %v", err)
|
||||
}
|
||||
if len(f.List()) == 0 {
|
||||
t.Error("List() is empty, want the module's categories")
|
||||
}
|
||||
}
|
||||
+19
-19
@@ -50,7 +50,7 @@ func TestShippedDataCategories(t *testing.T) {
|
||||
regexp.MustCompile(`^\d{1,3}( \d{3})?(,\d{2})? kr$`)},
|
||||
}
|
||||
|
||||
f := newGenerator(t, "data", WithSeed(1))
|
||||
f := shipped(t, WithSeed(1))
|
||||
for _, c := range cases {
|
||||
for i := 0; i < 200; i++ {
|
||||
if v := fake(t, f, "en_US."+c.path); !c.en.MatchString(v) {
|
||||
@@ -67,7 +67,7 @@ func TestShippedDataCategories(t *testing.T) {
|
||||
// v4 UUID (version/variant nibbles fixed), a MAC address, and credit-card numbers
|
||||
// whose trailing {luhn()} check passes.
|
||||
func TestShippedMiscCategories(t *testing.T) {
|
||||
f := newGenerator(t, "data/misc", WithSeed(1))
|
||||
f := newGenerator(t, "data/misc/misc", WithSeed(1))
|
||||
v4 := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`)
|
||||
mac := regexp.MustCompile(`^([0-9a-f]{2}:){5}[0-9a-f]{2}$`)
|
||||
objectid := regexp.MustCompile(`^[0-9a-f]{24}$`)
|
||||
@@ -95,7 +95,7 @@ func TestShippedMiscCategories(t *testing.T) {
|
||||
// codes follow their standard shape, dotted sub-paths resolve, emoji is a real
|
||||
// glyph, and a coordinate parses to a point within geographic bounds.
|
||||
func TestShippedMiscReferenceData(t *testing.T) {
|
||||
f := newGenerator(t, "data/misc", WithSeed(1))
|
||||
f := newGenerator(t, "data/misc/misc", WithSeed(1))
|
||||
re := map[string]*regexp.Regexp{
|
||||
"currency": regexp.MustCompile(`^[A-Z]{3}$`),
|
||||
"currency.name": regexp.MustCompile(`\p{L}`),
|
||||
@@ -158,7 +158,7 @@ func TestShippedMiscReferenceData(t *testing.T) {
|
||||
// TestSwedishPersonNamesHaveNoTripleLetter pins an orthographic rule the shape
|
||||
// regexes miss: no generated name repeats a character three times over.
|
||||
func TestSwedishPersonNamesHaveNoTripleLetter(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(11))
|
||||
f := shipped(t, WithSeed(11))
|
||||
for _, path := range []string{"sv_SE.person", "sv_SE.person.last"} {
|
||||
for i := 0; i < 20000; i++ {
|
||||
name := fake(t, f, path)
|
||||
@@ -177,7 +177,7 @@ func TestSwedishPersonNamesHaveNoTripleLetter(t *testing.T) {
|
||||
// male), the trailing digit is a Luhn checksum over the other nine, and a
|
||||
// samordningsnummer is the same with 60 added to the day.
|
||||
func TestSwedishPersonnummer(t *testing.T) {
|
||||
sv := newGenerator(t, "data", WithSeed(1))
|
||||
sv := shipped(t, WithSeed(1))
|
||||
re := regexp.MustCompile(`^\d{6}-23[89]\d$`)
|
||||
sawLongMonthEnd, birth := false, map[string]bool{}
|
||||
for i := 0; i < 2000; i++ {
|
||||
@@ -216,7 +216,7 @@ func TestSwedishPersonnummer(t *testing.T) {
|
||||
// 666, its group 01-99 and its serial 0001-9999; an ITIN is 9XX-GG-XXXX with GG
|
||||
// in 50-65, 70-88, 90-92 or 94-99.
|
||||
func TestShippedUSTaxIds(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(5))
|
||||
f := shipped(t, WithSeed(5))
|
||||
ssn := regexp.MustCompile(`^(\d{3})-(\d{2})-(\d{4})$`)
|
||||
itin := regexp.MustCompile(`^9\d{2}-(5\d|6[0-5]|7\d|8[0-8]|9[0-24-9])-\d{4}$`)
|
||||
for i := 0; i < 2000; i++ {
|
||||
@@ -240,7 +240,7 @@ func TestShippedUSTaxIds(t *testing.T) {
|
||||
// TestShippedPersonNames pins the name tables: a sex pins its first names, a name
|
||||
// both sexes carry appears under both, and the record's sex agrees with its name.
|
||||
func TestShippedPersonNames(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(9))
|
||||
f := shipped(t, WithSeed(9))
|
||||
for path, want := range map[string]string{
|
||||
"sv_SE.sex[f]": "kvinna",
|
||||
"sv_SE.sex[man].code": "m",
|
||||
@@ -281,7 +281,7 @@ func TestShippedPersonNames(t *testing.T) {
|
||||
// TestShippedPersonIsOneNamedPick pins that a person's first name, sex and title come
|
||||
// from one pick, and that two names bound to one person category are two people.
|
||||
func TestShippedPersonIsOneNamedPick(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(9))
|
||||
f := shipped(t, WithSeed(9))
|
||||
for _, locale := range []string{"sv_SE", "en_US"} {
|
||||
apart := false
|
||||
for i := 0; i < 300; i++ {
|
||||
@@ -302,7 +302,7 @@ func TestShippedPersonIsOneNamedPick(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestShippedSwedishPhone(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(11))
|
||||
f := shipped(t, WithSeed(11))
|
||||
re := regexp.MustCompile(`^0\d{1,2}-\d{3} \d{2} \d{2}$`)
|
||||
for i := 0; i < 50; i++ {
|
||||
if n := fake(t, f, "sv_SE.phone"); !re.MatchString(n) {
|
||||
@@ -312,7 +312,7 @@ func TestShippedSwedishPhone(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestShippedSwedishAddress(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(3))
|
||||
f := shipped(t, WithSeed(3))
|
||||
digit := regexp.MustCompile(`\d`)
|
||||
for i := 0; i < 30; i++ {
|
||||
a := fake(t, f, "sv_SE.address")
|
||||
@@ -333,7 +333,7 @@ func TestShippedSwedishAddress(t *testing.T) {
|
||||
// locality and region are one place in the geo tables, read through a name in a template or
|
||||
// across a struct's tags, and that a reference beside the name is another address.
|
||||
func TestShippedAddressIsOneNamedPick(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(3))
|
||||
f := shipped(t, WithSeed(3))
|
||||
for _, c := range []struct{ locale, country, template, largest string }{
|
||||
{"sv_SE", "SE", "{a.street}|{a.postal-code}|{a.locality}|", "Stockholm"},
|
||||
{"en_US", "US", "{a.street}|{a.postal-code}|{a.locality}|{a.region}", "New York"},
|
||||
@@ -379,7 +379,7 @@ func TestShippedAddressIsOneNamedPick(t *testing.T) {
|
||||
// one locality holds, the region empty for SE, whose address carries none.
|
||||
func geoPlaces(t *testing.T, country string) map[string]bool {
|
||||
rows := func(table string) [][]string {
|
||||
b, err := os.ReadFile("data/geo/" + country + "/" + table + ".tsv")
|
||||
b, err := os.ReadFile("data/geo/" + country + "/geo/" + country + "/" + table + ".tsv")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -417,7 +417,7 @@ func geoPlaces(t *testing.T, country string) map[string]bool {
|
||||
}
|
||||
|
||||
func TestShippedPersonHasParts(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(7))
|
||||
f := shipped(t, WithSeed(7))
|
||||
for _, path := range []string{"sv_SE.person", "en_US.person"} {
|
||||
for i := 0; i < 30; i++ {
|
||||
if name := fake(t, f, path); len(name) < 3 || !regexp.MustCompile(`\S \S`).MatchString(name) {
|
||||
@@ -429,7 +429,7 @@ func TestShippedPersonHasParts(t *testing.T) {
|
||||
|
||||
// NANPA's central office code records mark N11, 555, 950, 958, 959 and 976 unassignable.
|
||||
func TestShippedUSPhone(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(11))
|
||||
f := shipped(t, WithSeed(11))
|
||||
re := regexp.MustCompile(`^(?:\(\d{3}\) |\d{3}-)([2-9]\d{2})-\d{4}$`)
|
||||
unassignable := map[string]bool{"555": true, "950": true, "958": true, "959": true, "976": true}
|
||||
for i := 0; i < 2000; i++ {
|
||||
@@ -470,7 +470,7 @@ func TestShippedTableCellsAreTrimmed(t *testing.T) {
|
||||
// TestShippedNamespacedTree loads the whole data/ tree (not a single locale) and
|
||||
// reaches each locale through its folder segment: data/sv_SE/person -> sv_SE.person.
|
||||
func TestShippedNamespacedTree(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(1))
|
||||
f := shipped(t, WithSeed(1))
|
||||
for _, path := range []string{"sv_SE.person", "en_US.person", "sv_SE.address.locality"} {
|
||||
if got := fake(t, f, path); got == "" {
|
||||
t.Fatalf("Fake(%q) returned empty", path)
|
||||
@@ -514,7 +514,7 @@ func luhnValid(s string) bool {
|
||||
var swedishName = regexp.MustCompile(`^\p{L}+([ -]\p{L}+)*$`)
|
||||
|
||||
func TestShippedStreetIsARegisteredName(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(5))
|
||||
f := shipped(t, WithSeed(5))
|
||||
street := regexp.MustCompile(`^\p{L}[\p{L}\d:.-]*([ -][\p{L}\d:.-]+)*$`)
|
||||
for i := 0; i < 300; i++ {
|
||||
if s := fake(t, f, "sv_SE.address.street"); !street.MatchString(s) {
|
||||
@@ -526,7 +526,7 @@ func TestShippedStreetIsARegisteredName(t *testing.T) {
|
||||
func TestShippedLastNameComposition(t *testing.T) {
|
||||
// last is a choice of patronymic {first}sson templates, compound
|
||||
// {first}{last} templates and literal surnames.
|
||||
f := newGenerator(t, "data", WithSeed(6))
|
||||
f := shipped(t, WithSeed(6))
|
||||
for i := 0; i < 300; i++ {
|
||||
if s := fake(t, f, "sv_SE.person.last"); !swedishName.MatchString(s) {
|
||||
t.Fatalf("last name %q is not a Swedish surname", s)
|
||||
@@ -536,7 +536,7 @@ func TestShippedLastNameComposition(t *testing.T) {
|
||||
|
||||
func TestShippedStreetNumberFormats(t *testing.T) {
|
||||
// Reachable via a hyphenated path; covers all five weighted number variants.
|
||||
f := newGenerator(t, "data", WithSeed(8))
|
||||
f := shipped(t, WithSeed(8))
|
||||
re := regexp.MustCompile(`^[1-9]\d{0,2}[A-Z]?$`)
|
||||
for i := 0; i < 300; i++ {
|
||||
if n := fake(t, f, "sv_SE.address.street-number"); !re.MatchString(n) {
|
||||
@@ -548,7 +548,7 @@ func TestShippedStreetNumberFormats(t *testing.T) {
|
||||
// TestShippedUSTitleAgreesWithSex pins that one person's title and sex agree, and
|
||||
// that the everyday titles are reachable.
|
||||
func TestShippedUSTitleAgreesWithSex(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(4))
|
||||
f := shipped(t, WithSeed(4))
|
||||
female := map[string]bool{"Miss": true, "Mrs": true, "Ms": true}
|
||||
seen := map[string]bool{}
|
||||
for i := 0; i < 3000; i++ {
|
||||
|
||||
+4
-4
@@ -88,7 +88,7 @@ func TestDatatypeRejectsAValueItsTypeRejects(t *testing.T) {
|
||||
for name, body := range tree {
|
||||
files[name] = body
|
||||
}
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, files)))
|
||||
_, err := New(WithDataPath(writeData(t, files)))
|
||||
if err == nil || !strings.Contains(err.Error(), c.want) {
|
||||
t.Errorf("%s: New = %v, want an error containing %q", c.name, err, c.want)
|
||||
}
|
||||
@@ -102,14 +102,14 @@ func TestDatatypeRejectsAValueItsTypeRejects(t *testing.T) {
|
||||
`[{"format":"1","datatype":"integer"},{"format":"1.5","datatype":"number"}]`: "its items hold integer and number; a column holds one datatype",
|
||||
} {
|
||||
row := `{"format":"","col":` + column + `}`
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{"row": row}))); err == nil || !strings.HasSuffix(err.Error(), want) {
|
||||
if _, err := New(WithDataPath(writeData(t, map[string]string{"row": row}))); err == nil || !strings.HasSuffix(err.Error(), want) {
|
||||
t.Errorf("items of two declared datatypes %s: New = %v, want an error ending %q", column, err, want)
|
||||
}
|
||||
if _, err := newGenerator(t, writeData(t, tree)).NewTemplate(row); err == nil || !strings.HasSuffix(err.Error(), want) {
|
||||
t.Errorf("items of two declared datatypes %s: NewTemplate = %v, want an error ending %q", column, err, want)
|
||||
}
|
||||
}
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{
|
||||
"a": `{"format":"","c":["{/b.x}",{"format":"1","datatype":"integer"}]}`,
|
||||
"b": `{"format":"","x":["2",{"format":"1","datatype":"integer"}]}`,
|
||||
})))
|
||||
@@ -148,7 +148,7 @@ func TestDatatypeAcceptsAColumnThatAlwaysParses(t *testing.T) {
|
||||
} {
|
||||
row := `{"format":"","col":` + column + `}`
|
||||
src := `{"format":"","code":["200","404"],"n":{"format":"{int(1,9)}","datatype":"integer"}}`
|
||||
f, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{"cat": cat, "row": row, "src": src})), WithSeed(1))
|
||||
f, err := New(WithDataPath(writeData(t, map[string]string{"cat": cat, "row": row, "src": src})), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Errorf("%s: New = %v, want it loaded", column, err)
|
||||
continue
|
||||
|
||||
+1
-1
@@ -265,7 +265,7 @@ func TestGoExamplesPrintWhatTheyRender(t *testing.T) {
|
||||
if lines == nil {
|
||||
t.Errorf("%s prints no seeded Fake, so nothing holds its example to a render", name)
|
||||
}
|
||||
f, err := New(WithSeed(42))
|
||||
f, err := New(withShipped(), WithSeed(42))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
+9
-6
@@ -7,10 +7,13 @@ import (
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"github.com/larvit/fejkdata/data"
|
||||
"github.com/larvit/fejkdata/internal/drawstate"
|
||||
"github.com/larvit/fejkdata/internal/jsonvalue"
|
||||
)
|
||||
|
||||
func withShipped() Option { return WithDataFS(data.Modules()...) }
|
||||
|
||||
// newGenerator creates a generator over a single data directory, failing on
|
||||
// error. Most tests load one dir; newGeneratorN loads several (last-loaded wins
|
||||
// on conflicts).
|
||||
@@ -21,7 +24,7 @@ func newGenerator(t *testing.T, dir string, opts ...Option) *Generator {
|
||||
|
||||
func newGeneratorN(t *testing.T, dirs []string, opts ...Option) *Generator {
|
||||
t.Helper()
|
||||
all := []Option{WithoutShippedData()}
|
||||
var all []Option
|
||||
for _, dir := range dirs {
|
||||
all = append(all, WithDataPath(dir))
|
||||
}
|
||||
@@ -43,14 +46,14 @@ func fake(t *testing.T, f *Generator, path string) string {
|
||||
}
|
||||
|
||||
func TestNewMissingDirectory(t *testing.T) {
|
||||
_, err := New(WithoutShippedData(), WithDataPath("data/de_DE"))
|
||||
_, err := New(WithDataPath("data/de_DE"))
|
||||
if err == nil || !strings.Contains(err.Error(), "de_DE") || !errors.Is(err, fs.ErrNotExist) {
|
||||
t.Fatalf("New(missing) error = %v, want the real error, naming the path", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRejectsAnEmptyDataPath(t *testing.T) {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(""))
|
||||
_, err := New(WithDataPath(""))
|
||||
if err == nil || !strings.Contains(err.Error(), "empty") {
|
||||
t.Fatalf("New(WithDataPath(\"\")) = %v, want the empty path named", err)
|
||||
}
|
||||
@@ -60,14 +63,14 @@ func TestNewReportsAnEntropyFailure(t *testing.T) {
|
||||
saved := randomBytes
|
||||
randomBytes = func([]byte) (int, error) { return 0, errors.New("no entropy") }
|
||||
defer func() { randomBytes = saved }()
|
||||
_, err := New(WithoutShippedData(), WithDataFS(fstest.MapFS{"w.json": {Data: []byte(`"x"`)}}))
|
||||
_, err := New(WithDataFS(fstest.MapFS{"w.json": {Data: []byte(`"x"`)}}))
|
||||
if err == nil || !strings.Contains(err.Error(), "no entropy") {
|
||||
t.Fatalf("New() without entropy = %v, want the failure reported", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithSeedIsDeterministic(t *testing.T) {
|
||||
a, b := newGenerator(t, "data", WithSeed(42)), newGenerator(t, "data", WithSeed(42))
|
||||
a, b := shipped(t, WithSeed(42)), shipped(t, WithSeed(42))
|
||||
for i := 0; i < 50; i++ {
|
||||
if x, y := fake(t, a, "sv_SE.person"), fake(t, b, "sv_SE.person"); x != y {
|
||||
t.Fatalf("same seed diverged at %d: %q != %q", i, x, y)
|
||||
@@ -76,7 +79,7 @@ func TestWithSeedIsDeterministic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDifferentSeedsDiffer(t *testing.T) {
|
||||
a, b := newGenerator(t, "data", WithSeed(1)), newGenerator(t, "data", WithSeed(2))
|
||||
a, b := shipped(t, WithSeed(1)), shipped(t, WithSeed(2))
|
||||
for i := 0; i < 50; i++ {
|
||||
if fake(t, a, "en_US.person") != fake(t, b, "en_US.person") {
|
||||
return
|
||||
|
||||
+13
-1
@@ -30,7 +30,13 @@ func TestPackageImports(t *testing.T) {
|
||||
checked := 0
|
||||
allowed := map[string][]string{
|
||||
".": {"internal/builtinfunc", "internal/datafiles", "internal/datatype", "internal/drawstate", "internal/grammar", "internal/invariant", "internal/jsonvalue", "internal/proven", "internal/rows"},
|
||||
"cmd/fejkdata": {"."},
|
||||
"cmd/fejkdata": {".", "data"},
|
||||
"data": {"data/en_US", "data/geo/SE", "data/geo/US", "data/misc", "data/sv_SE"},
|
||||
"data/en_US": nil,
|
||||
"data/geo/SE": nil,
|
||||
"data/geo/US": nil,
|
||||
"data/misc": nil,
|
||||
"data/sv_SE": nil,
|
||||
"internal/builtinfunc": {"internal/datatype", "internal/drawstate", "internal/grammar", "internal/invariant", "internal/proven"},
|
||||
"internal/datafiles": {"internal/grammar", "internal/jsonvalue"},
|
||||
"internal/datatype": nil,
|
||||
@@ -42,7 +48,13 @@ func TestPackageImports(t *testing.T) {
|
||||
"internal/rows": {"internal/drawstate", "internal/grammar", "internal/invariant"},
|
||||
}
|
||||
testsAlso := map[string][]string{
|
||||
".": {"data", "data/sv_SE"},
|
||||
"cmd/fejkdata": {"internal/grammar"},
|
||||
"data/en_US": {"."},
|
||||
"data/geo/SE": {"."},
|
||||
"data/geo/US": {"."},
|
||||
"data/misc": {"."},
|
||||
"data/sv_SE": {"."},
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
eachPackage(t, func(dir string, pkg *build.Package) {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ func tmpl(t *testing.T, f *Generator, input string) string {
|
||||
|
||||
func shipped(t *testing.T, opts ...Option) *Generator {
|
||||
t.Helper()
|
||||
f, err := New(append([]Option{WithSeed(1)}, opts...)...)
|
||||
f, err := New(append([]Option{withShipped(), WithSeed(1)}, opts...)...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
+7
-7
@@ -139,7 +139,7 @@ func TestMultiPathMergesFolders(t *testing.T) {
|
||||
// tree: everything List advertises renders, every time, and the sub-fields the
|
||||
// README advertises are discoverable.
|
||||
func TestListedPathsAllRender(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(4))
|
||||
f := shipped(t, WithSeed(4))
|
||||
paths := f.List()
|
||||
for _, p := range paths {
|
||||
for i := 0; i < 20; i++ {
|
||||
@@ -187,12 +187,12 @@ func TestRepeatProductAlongAPathIsCapped(t *testing.T) {
|
||||
"b": `{"format":"x","repeat":2048}`,
|
||||
},
|
||||
} {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, files)))
|
||||
_, err := New(WithDataPath(writeData(t, files)))
|
||||
if err == nil || !strings.Contains(err.Error(), "repeat") || !strings.Contains(err.Error(), "1048576") {
|
||||
t.Errorf("%s: New = %v, want the repeat product rejected naming the maximum", name, err)
|
||||
}
|
||||
}
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{
|
||||
if _, err := New(WithDataPath(writeData(t, map[string]string{
|
||||
"cat": `{"format":"{a}{c}","repeat":1024,"a":{"format":"{b}","repeat":1024,"b":"x"},"c":{"format":"y","repeat":1024}}`,
|
||||
}))); err != nil {
|
||||
t.Errorf("New = %v, want 1024 x 1024 along one path accepted", err)
|
||||
@@ -205,11 +205,11 @@ func TestNewErrors(t *testing.T) {
|
||||
if err := os.WriteFile(file, []byte("{}"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(file)); err == nil || !strings.Contains(err.Error(), "xx_XX is not a directory") {
|
||||
if _, err := New(WithDataPath(file)); err == nil || !strings.Contains(err.Error(), "xx_XX is not a directory") {
|
||||
t.Errorf("New(file) = %v, want not-a-directory error", err)
|
||||
}
|
||||
// Invalid JSON in a category file fails.
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{"broken": `{ not json`}))); err == nil {
|
||||
if _, err := New(WithDataPath(writeData(t, map[string]string{"broken": `{ not json`}))); err == nil {
|
||||
t.Error("New(invalid JSON) = nil error")
|
||||
}
|
||||
// An option that cannot take effect, and a category or folder no dot path can
|
||||
@@ -300,7 +300,7 @@ func TestNewErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for name, c := range rejected {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, c.files)))
|
||||
_, err := New(WithDataPath(writeData(t, c.files)))
|
||||
if err == nil {
|
||||
t.Errorf("%s: New = nil error, want it rejected at load", name)
|
||||
continue
|
||||
@@ -328,7 +328,7 @@ func TestNewErrors(t *testing.T) {
|
||||
"repeated reference arm": {map[string]string{"a": `"x"`, "b": `"{/a|/a}"`}, "b", "x"},
|
||||
}
|
||||
for name, c := range accepted {
|
||||
f, err := New(WithoutShippedData(), WithDataPath(writeData(t, c.files)))
|
||||
f, err := New(WithDataPath(writeData(t, c.files)))
|
||||
if err != nil {
|
||||
t.Errorf("%s: New = %v, want it accepted", name, err)
|
||||
continue
|
||||
|
||||
+2
-2
@@ -253,7 +253,7 @@ func TestNameErrors(t *testing.T) {
|
||||
}
|
||||
files[name] = body
|
||||
}
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, files)))
|
||||
_, err := New(WithDataPath(writeFiles(t, files)))
|
||||
if c.want == "" {
|
||||
if err != nil {
|
||||
t.Fatalf("New() = %v, want it to load", err)
|
||||
@@ -463,7 +463,7 @@ func TestARowThroughANameHintsAPathThatLoads(t *testing.T) {
|
||||
files := nameTables()
|
||||
files["card.json"] = `{"format":"{x as r}{r[01]}","x":"{/region}"}`
|
||||
want := `a path through name "r" may not select a row; bind the row to a name of its own`
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) {
|
||||
if _, err := New(WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("New(a field-bound name selecting a row) = %v, want %s", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ func TestAnOptionOfTheWrongKindIsNamedByItsJSONKind(t *testing.T) {
|
||||
t.Errorf("compile(%s) = %v, want %q", src, err, want)
|
||||
}
|
||||
}
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","key":5}`, "t.tsv": "a\nx\n"})))
|
||||
_, err := New(WithDataPath(writeFiles(t, map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","key":5}`, "t.tsv": "a\nx\n"})))
|
||||
if err == nil || !strings.Contains(err.Error(), "key must be a string, not a number") {
|
||||
t.Errorf("key 5 = %v, want the kind named", err)
|
||||
}
|
||||
|
||||
+2
-2
@@ -130,7 +130,7 @@ func TestPathKeyIsUnambiguous(t *testing.T) {
|
||||
dir := writeData(t, map[string]string{
|
||||
"cat": `[{"format":"{a.b}","a.b":"1"},{"format":"{a}","a":{"format":"{b}","b":"2"}}]`,
|
||||
})
|
||||
_, err := New(WithoutShippedData(), WithDataPath(dir))
|
||||
_, err := New(WithDataPath(dir))
|
||||
if err == nil || !strings.Contains(err.Error(), `field "a.b" contains "."`) {
|
||||
t.Fatalf("New = %v, want the dotted field name rejected", err)
|
||||
}
|
||||
@@ -150,7 +150,7 @@ func TestPathKeyIsUnambiguous(t *testing.T) {
|
||||
// single-variant choice always draws the same item, so it needs no every-variant
|
||||
// guard and the error can name the field that is missing.
|
||||
func TestMissingFieldNamesItself(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(1))
|
||||
f := shipped(t, WithSeed(1))
|
||||
_, err := f.Fake("sv_SE.person.typo")
|
||||
if err == nil || !strings.Contains(err.Error(), `no field "typo"`) {
|
||||
t.Errorf("Fake(person.typo) = %v, want it to name the missing field", err)
|
||||
|
||||
+17
-13
@@ -44,7 +44,7 @@ func TestNoRenderAllocRegression(t *testing.T) {
|
||||
{"format":"y","a":"A","b":"B","c":"C","d":"D","e":"E","f":"F","g":"G","h":"H","i":"I","j":"J"}]}`, 3},
|
||||
}
|
||||
for _, s := range shapes {
|
||||
f, err := New(WithoutShippedData(), WithDataFS(fstest.MapFS{"x.json": {Data: []byte(s.json)}}))
|
||||
f, err := New(WithDataFS(fstest.MapFS{"x.json": {Data: []byte(s.json)}}))
|
||||
if err != nil {
|
||||
t.Fatalf("New(%s): %v", s.name, err)
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func TestNoReferenceAllocRegression(t *testing.T) {
|
||||
{"a repeat of a name", `{"format":"{r}","r":{"format":"{/word as n}{n.w}{n.w}","repeat":20,"separator":", "}}`, 162, 1.10},
|
||||
{"a read two levels under a pick", `{"format":"{place.geo as g}{g} {g.town.zip}","place":{"format":"{geo}","geo":{"format":"{town.zip}","town":{"format":"{zip}","zip":"1"}}}}`, 12, 1},
|
||||
} {
|
||||
f, err := New(WithoutShippedData(), WithDataFS(fstest.MapFS{
|
||||
f, err := New(WithDataFS(fstest.MapFS{
|
||||
"word.json": {Data: []byte(word)},
|
||||
"x.json": {Data: []byte(s.json)},
|
||||
}))
|
||||
@@ -81,7 +81,7 @@ func TestNoReferenceAllocRegression(t *testing.T) {
|
||||
|
||||
// A table read pins a row inline and reads its cells in place.
|
||||
func TestNoTableAllocRegression(t *testing.T) {
|
||||
f, err := New(WithoutShippedData(), WithDataFS(fstest.MapFS{
|
||||
f, err := New(WithDataFS(fstest.MapFS{
|
||||
"region.json": {Data: []byte(`{"format":"{name}","rows":"region.tsv","key":"code","weight":"population"}`)},
|
||||
"region.tsv": {Data: []byte("code\tname\tpopulation\n01\tStockholms län\t2400000\n12\tSkåne län\t1400000\n14\tVästra Götalands län\t1750000\n")},
|
||||
"x.json": {Data: []byte(`"{/region.name}, {/region.code}"`)},
|
||||
@@ -113,7 +113,7 @@ func TestNoTableAllocRegression(t *testing.T) {
|
||||
}
|
||||
deep[name+".json"], deep[name+".tsv"] = &fstest.MapFile{Data: []byte(category)}, &fstest.MapFile{Data: []byte(rows)}
|
||||
}
|
||||
f, err = New(WithoutShippedData(), WithDataFS(deep))
|
||||
f, err = New(WithDataFS(deep))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -129,7 +129,7 @@ func TestNoRecordAllocRegression(t *testing.T) {
|
||||
{"record 3 columns", `{"format":"","a":"x","b":"y","c":"z"}`},
|
||||
{"record 50 columns", wideTokenJSON(50)},
|
||||
} {
|
||||
f, err := New(WithoutShippedData(), WithDataFS(fstest.MapFS{"x.json": {Data: []byte(s.json)}}))
|
||||
f, err := New(WithDataFS(fstest.MapFS{"x.json": {Data: []byte(s.json)}}))
|
||||
if err != nil {
|
||||
t.Fatalf("New(%s): %v", s.name, err)
|
||||
}
|
||||
@@ -148,7 +148,7 @@ func TestNoRecordAllocRegression(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNoStructAllocRegression(t *testing.T) {
|
||||
f, err := New(WithoutShippedData(), WithDataFS(fstest.MapFS{"x.json": {Data: []byte(`{"format":"","a":"x","b":"y","c":"z"}`)}}))
|
||||
f, err := New(WithDataFS(fstest.MapFS{"x.json": {Data: []byte(`{"format":"","a":"x","b":"y","c":"z"}`)}}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -166,20 +166,24 @@ func TestNoStructAllocRegression(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNestedDepth25(b *testing.B) { benchPath(b, tmpData(b, "deep", nestedJSON(25)), "deep") }
|
||||
func BenchmarkNestedDepth100(b *testing.B) { benchPath(b, tmpData(b, "deep", nestedJSON(100)), "deep") }
|
||||
func BenchmarkNestedDepth25(b *testing.B) {
|
||||
benchPath(b, WithDataPath(tmpData(b, "deep", nestedJSON(25))), "deep")
|
||||
}
|
||||
func BenchmarkNestedDepth100(b *testing.B) {
|
||||
benchPath(b, WithDataPath(tmpData(b, "deep", nestedJSON(100))), "deep")
|
||||
}
|
||||
func BenchmarkWideTokens100(b *testing.B) {
|
||||
benchPath(b, tmpData(b, "wide", wideTokenJSON(100)), "wide")
|
||||
benchPath(b, WithDataPath(tmpData(b, "wide", wideTokenJSON(100))), "wide")
|
||||
}
|
||||
func BenchmarkWideTokens500(b *testing.B) {
|
||||
benchPath(b, tmpData(b, "wide", wideTokenJSON(500)), "wide")
|
||||
benchPath(b, WithDataPath(tmpData(b, "wide", wideTokenJSON(500))), "wide")
|
||||
}
|
||||
|
||||
// New decodes the shipped manifest and loads nothing else.
|
||||
// New decodes the shipped modules' manifests and loads nothing else.
|
||||
func TestNoNewAllocRegression(t *testing.T) {
|
||||
const base = 2449.0
|
||||
const base = 2864.0
|
||||
allocs := testing.AllocsPerRun(100, func() {
|
||||
if _, err := New(WithSeed(1)); err != nil {
|
||||
if _, err := New(withShipped(), WithSeed(1)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
+10
-10
@@ -152,7 +152,7 @@ func TestANameReadOnceIsWhatItBinds(t *testing.T) {
|
||||
func TestCycleReachedOnlyByAPathTokenIsRejected(t *testing.T) {
|
||||
// A path token renders what it lands on, not the level it started from, so the
|
||||
// cycle walk has to follow it there.
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{
|
||||
"a": `{"format":"{p.x}","p":{"format":"static","x":"{/b}"}}`,
|
||||
"b": `"{/a}"`,
|
||||
})))
|
||||
@@ -162,7 +162,7 @@ func TestCycleReachedOnlyByAPathTokenIsRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestACycleInALaterVariantIsRejected(t *testing.T) {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{
|
||||
"cat": `{"format":"{p.x}","p":[{"format":"h","x":"safe"},{"format":"h","x":"{/hop}"}]}`,
|
||||
"hop": `"{/cat}"`,
|
||||
})))
|
||||
@@ -172,7 +172,7 @@ func TestACycleInALaterVariantIsRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCycleThroughAPathTokenIsRejected(t *testing.T) {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{
|
||||
"a": `{"format":"{p.x}","p":{"format":"{x}","x":"{/b}"}}`,
|
||||
"b": `"{/a}"`,
|
||||
})))
|
||||
@@ -190,7 +190,7 @@ func TestADeepDiamondChainLoads(t *testing.T) {
|
||||
`{"format":"{a}{b}","a":"{/l%d}","b":"{/l%d}"}`, i-1, i-1)
|
||||
}
|
||||
files["thing"] = `{"format":"{p.first} {/l30}","p":{"format":"x","first":["A","B"]}}`
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeData(t, files))); err != nil {
|
||||
if _, err := New(WithDataPath(writeData(t, files))); err != nil {
|
||||
t.Fatalf("New = %v, want a deep diamond chain to load", err)
|
||||
}
|
||||
}
|
||||
@@ -208,7 +208,7 @@ func TestNestedChoiceDrawsOneVariant(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRepeatingLevelIsNamedInTheError(t *testing.T) {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{
|
||||
"cat": `{"format":"[{p.a.b}]","p":{"format":"{a}","a":{"format":"{b}","repeat":3,"separator":",","b":"z"}}}`,
|
||||
})))
|
||||
if err == nil || !strings.Contains(err.Error(), `"p.a"`) {
|
||||
@@ -223,7 +223,7 @@ func TestPathIntoARepeatingLevelIsRejected(t *testing.T) {
|
||||
`{"format":"[{p.a}]","p":{"format":"{a}","repeat":3,"separator":",","a":"z"}}`,
|
||||
`{"format":"[{p.a}]","p":[{"format":"{a}","repeat":3,"separator":",","a":"x"},{"format":"{a}","a":"y"}]}`,
|
||||
} {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{"cat": cat})))
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{"cat": cat})))
|
||||
if err == nil || !strings.Contains(err.Error(), "repeat") {
|
||||
t.Errorf("New(%s) = %v, want a path into a repeating level rejected", cat, err)
|
||||
}
|
||||
@@ -231,7 +231,7 @@ func TestPathIntoARepeatingLevelIsRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPathIntoAPlainTemplateNamesTheMissingField(t *testing.T) {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{
|
||||
"cat": `{"format":"{a.nope}","a":{"format":"x","b":"1"}}`,
|
||||
})))
|
||||
if err == nil || !strings.Contains(err.Error(), `no field "nope"`) {
|
||||
@@ -273,7 +273,7 @@ func TestDottedTokenErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for name, c := range rejected {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{"cat": c.file})))
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{"cat": c.file})))
|
||||
if err == nil || !strings.Contains(err.Error(), c.want) {
|
||||
t.Errorf("%s: New = %v, want it to mention %q", name, err, c.want)
|
||||
}
|
||||
@@ -285,7 +285,7 @@ func TestEmptyPathSegmentIsRejected(t *testing.T) {
|
||||
"trailing dot": `{"format":"[{a.}]","a":"x"}`,
|
||||
"triple dot": `{"format":"[{a...b}]","a":"x"}`,
|
||||
} {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{"cat": file})))
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{"cat": file})))
|
||||
if err == nil || !strings.Contains(err.Error(), "empty segment") {
|
||||
t.Errorf("%s: New = %v, want it to name the empty segment", name, err)
|
||||
}
|
||||
@@ -294,7 +294,7 @@ func TestEmptyPathSegmentIsRejected(t *testing.T) {
|
||||
|
||||
func TestASubFieldIsReachableByFake(t *testing.T) {
|
||||
dir := writeData(t, map[string]string{"address": places("{place as p}{p.postal-code} {p.locality}")})
|
||||
f, err := New(WithoutShippedData(), WithDataPath(dir), WithSeed(9))
|
||||
f, err := New(WithDataPath(dir), WithSeed(9))
|
||||
if err != nil {
|
||||
t.Fatalf("New = %v", err)
|
||||
}
|
||||
|
||||
+5
-5
@@ -65,7 +65,7 @@ func TestReadmeExamplesLoadAndRender(t *testing.T) {
|
||||
if strings.Contains(body, "…") {
|
||||
continue
|
||||
}
|
||||
f, err := New(WithDataPath(writeFiles(t, exampleFiles(t, src, at[0], body))), WithSeed(1))
|
||||
f, err := New(withShipped(), WithDataPath(writeFiles(t, exampleFiles(t, src, at[0], body))), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Errorf("README example does not load: %v\n%s", err, body)
|
||||
continue
|
||||
@@ -91,7 +91,7 @@ func TestReadmeRecordExample(t *testing.T) {
|
||||
if block == nil {
|
||||
t.Fatal("README lost the Records example")
|
||||
}
|
||||
f, err := New(WithDataPath(writeData(t, map[string]string{"users": block[1]})), WithSeed(1))
|
||||
f, err := New(withShipped(), WithDataPath(writeData(t, map[string]string{"users": block[1]})), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -119,7 +119,7 @@ func TestReadmeSQLExampleOutput(t *testing.T) {
|
||||
if block == nil || want == nil {
|
||||
t.Fatal("README lost the SQL example or its printed output")
|
||||
}
|
||||
f, err := New(WithDataPath(writeData(t, map[string]string{"sql": block[1]})), WithSeed(1))
|
||||
f, err := New(withShipped(), WithDataPath(writeData(t, map[string]string{"sql": block[1]})), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func TestReadmeDatatypeExample(t *testing.T) {
|
||||
t.Fatal("README lost the Datatype section")
|
||||
}
|
||||
block := jsonBlock.FindStringSubmatch(src[i:])
|
||||
f, err := New(WithDataPath(writeData(t, map[string]string{"order": block[1]})), WithSeed(1))
|
||||
f, err := New(withShipped(), WithDataPath(writeData(t, map[string]string{"order": block[1]})), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -179,7 +179,7 @@ func TestReadmeTableExample(t *testing.T) {
|
||||
t.Fatal("README Table section has no json block")
|
||||
}
|
||||
body := src[i:][at[2]:at[3]]
|
||||
f, err := New(WithDataPath(writeFiles(t, exampleFiles(t, src, i+at[0], body))), WithSeed(1))
|
||||
f, err := New(withShipped(), WithDataPath(writeFiles(t, exampleFiles(t, src, i+at[0], body))), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
+1
-1
@@ -530,7 +530,7 @@ func TestRecordTemplateRejectsATopLevelRepeat(t *testing.T) {
|
||||
// command a fixture author types: a category of one value has no columns, and the
|
||||
// error names the record that gives it one.
|
||||
func TestRecordOfAFieldlessCategoryNamesTheWrapper(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(1))
|
||||
f := shipped(t, WithSeed(1))
|
||||
want := `{"format":"","personnummer":"{/sv_SE.personnummer}"}`
|
||||
_, err := f.FakeRecord("sv_SE.personnummer")
|
||||
if err == nil || !strings.Contains(err.Error(), want) {
|
||||
|
||||
+11
-11
@@ -107,7 +107,7 @@ func TestReferenceErrors(t *testing.T) {
|
||||
"field key using the reference prefix": {"cat": `{"format":"hi","..x":"{/nope}"}`},
|
||||
}
|
||||
for name, files := range cases {
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeData(t, files))); err == nil {
|
||||
if _, err := New(WithDataPath(writeData(t, files))); err == nil {
|
||||
t.Errorf("%s: New = nil error, want a reference error", name)
|
||||
}
|
||||
}
|
||||
@@ -142,13 +142,13 @@ func TestAReferenceIntoItsOwnCategoryIsAFreshDraw(t *testing.T) {
|
||||
"the category in its format": `{"format":"{/cat}"}`,
|
||||
"a field reading itself": `{"format":"{x}","x":"{/cat.x}"}`,
|
||||
} {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{"cat": file})))
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{"cat": file})))
|
||||
if err == nil || !strings.Contains(err.Error(), "cycle") {
|
||||
t.Errorf("%s: New = %v, want the cycle refused", name, err)
|
||||
}
|
||||
}
|
||||
// Reading a sibling as a path is the spelling that stays.
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{
|
||||
if _, err := New(WithDataPath(writeData(t, map[string]string{
|
||||
"cat": `{"format":"{y.v}","y":{"format":"{v}","v":["1","2"]}}`,
|
||||
}))); err != nil {
|
||||
t.Errorf("New = %v, want the sibling path accepted", err)
|
||||
@@ -173,7 +173,7 @@ func TestNewErrorIsDeterministic(t *testing.T) {
|
||||
dir := writeData(t, files)
|
||||
var first string
|
||||
for i := 0; i < 50; i++ {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(dir))
|
||||
_, err := New(WithDataPath(dir))
|
||||
if err == nil {
|
||||
t.Fatalf("%s: New = nil error, want a load error", name)
|
||||
}
|
||||
@@ -206,11 +206,11 @@ func TestNewErrorPathIsCanonical(t *testing.T) {
|
||||
{
|
||||
"bad reference reached through another reference",
|
||||
map[string]string{"a": `"{/b}"`, "b": `"{/nope}"`},
|
||||
`fejkdata: b: reference {/nope}: no entry "nope"`,
|
||||
`fejkdata: b: reference {/nope}: no entry "nope", and the module holding b names no module it reads by default`,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, c.files)))
|
||||
_, err := New(WithDataPath(writeData(t, c.files)))
|
||||
if err == nil {
|
||||
t.Errorf("%s: New = nil error", c.name)
|
||||
continue
|
||||
@@ -222,7 +222,7 @@ func TestNewErrorPathIsCanonical(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReferenceThroughChoiceNeedsEveryVariant(t *testing.T) {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{
|
||||
_, err := New(WithDataPath(writeData(t, map[string]string{
|
||||
"who": `[{"format":"{f}{h}","f":"1","h":"x"},{"format":"{g}{h}","g":"2","h":"y"}]`,
|
||||
"card": `"{/who.f}"`,
|
||||
})))
|
||||
@@ -255,7 +255,7 @@ func TestReferencesIntoOneLevelLoad(t *testing.T) {
|
||||
{"row": `{"format":"{p.first} {/hop}","p":[{"format":"{first}","first":"A","last":"1"},{"format":"{first}","first":"B","last":"2"}]}`, "hop": `"{/row.p.last}"`},
|
||||
{"sv_SE/person": cat, "sv_SE/mail": `"{.person} <{/sv_SE.person.p.first}>"`},
|
||||
} {
|
||||
f, err := New(WithoutShippedData(), WithDataPath(writeData(t, files)), WithSeed(1))
|
||||
f, err := New(WithDataPath(writeData(t, files)), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Errorf("New(%v) = %v, want each read a draw of its own", files, err)
|
||||
continue
|
||||
@@ -325,7 +325,7 @@ func TestReferenceSigilErrors(t *testing.T) {
|
||||
"missing sibling": {"sv_SE/a": `"{.nope}"`},
|
||||
"slash in a field name": {"a": `{"format":"{x}","x":"1","a/b":"2"}`},
|
||||
} {
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeData(t, files))); err == nil {
|
||||
if _, err := New(WithDataPath(writeData(t, files))); err == nil {
|
||||
t.Errorf("%s: New = nil error, want a reference error", name)
|
||||
}
|
||||
}
|
||||
@@ -336,7 +336,7 @@ func TestSlashAfterAReferenceSigilIsRejected(t *testing.T) {
|
||||
"after ..": {"sv_SE/person": `"Ada"`, "sv_SE/deep/a": `"{../person}"`},
|
||||
"after .": {"sv_SE/person": `"Ada"`, "sv_SE/a": `"{./person}"`},
|
||||
} {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, files)))
|
||||
_, err := New(WithDataPath(writeData(t, files)))
|
||||
if err == nil || !strings.Contains(err.Error(), "person}") || !strings.Contains(err.Error(), "write {") {
|
||||
t.Errorf("%s: New = %v, want the slash rejected naming the spelling", name, err)
|
||||
}
|
||||
@@ -344,7 +344,7 @@ func TestSlashAfterAReferenceSigilIsRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestACycleThroughASelectedRowIsRefused(t *testing.T) {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, map[string]string{
|
||||
_, err := New(WithDataPath(writeFiles(t, map[string]string{
|
||||
"x.json": `{"format":"{v} {/y}","rows":"x.tsv","key":"code"}`,
|
||||
"x.tsv": "code\tv\n1\ta\n2\tb\n",
|
||||
"y.json": `"{/x[1]}"`,
|
||||
|
||||
+30
-10
@@ -9,8 +9,16 @@ import (
|
||||
"testing/fstest"
|
||||
)
|
||||
|
||||
func TestNewDefaultsToShippedData(t *testing.T) {
|
||||
f, err := New(WithSeed(1))
|
||||
func TestABareNewFailsNamingWithDataFS(t *testing.T) {
|
||||
for name, opts := range map[string][]Option{"no option": nil, "only a seed": {WithSeed(1)}} {
|
||||
if _, err := New(opts...); err == nil || !strings.Contains(err.Error(), "WithDataFS(data.Modules()...)") {
|
||||
t.Errorf("New(%s) = %v, want it refused, naming WithDataFS(data.Modules()...)", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEveryShippedModuleLoads(t *testing.T) {
|
||||
f, err := New(withShipped(), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatalf("New() = %v", err)
|
||||
}
|
||||
@@ -27,7 +35,7 @@ func TestNewDefaultsToShippedData(t *testing.T) {
|
||||
|
||||
func TestWithDataPathLayersOverShipped(t *testing.T) {
|
||||
dir := writeData(t, map[string]string{"sv_SE/word": `"only-mine"`, "greeting": `"hej"`})
|
||||
f, err := New(WithDataPath(dir), WithSeed(1))
|
||||
f, err := New(withShipped(), WithDataPath(dir), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatalf("New = %v", err)
|
||||
}
|
||||
@@ -44,7 +52,7 @@ func TestWithDataPathLayersOverShipped(t *testing.T) {
|
||||
|
||||
func TestUserDataMayReferenceShipped(t *testing.T) {
|
||||
dir := writeData(t, map[string]string{"greeting": `"Hej {/sv_SE.person}!"`})
|
||||
f, err := New(WithDataPath(dir), WithSeed(1))
|
||||
f, err := New(withShipped(), WithDataPath(dir), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatalf("New = %v", err)
|
||||
}
|
||||
@@ -61,8 +69,8 @@ func TestAGeneratorWithDataSaysNothingOfNoData(t *testing.T) {
|
||||
|
||||
func TestAGeneratorWithNoDataRendersWhatReadsNone(t *testing.T) {
|
||||
for name, opts := range map[string][]Option{
|
||||
"no source": {WithoutShippedData()},
|
||||
"empty folder": {WithoutShippedData(), WithDataPath(t.TempDir())},
|
||||
"no module": {WithDataFS()},
|
||||
"empty folder": {WithDataPath(t.TempDir())},
|
||||
} {
|
||||
f, err := New(opts...)
|
||||
if err != nil {
|
||||
@@ -83,7 +91,7 @@ func TestAGeneratorWithNoDataRendersWhatReadsNone(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithoutShippedDataListsOnlyOwn(t *testing.T) {
|
||||
func TestADataPathAloneListsOnlyItsOwn(t *testing.T) {
|
||||
dir := writeData(t, map[string]string{"greeting": `"hej"`})
|
||||
f := newGenerator(t, dir, WithSeed(1))
|
||||
if got := f.List(); !reflect.DeepEqual(got, []string{"greeting"}) {
|
||||
@@ -98,7 +106,7 @@ func TestWithDataFS(t *testing.T) {
|
||||
".hidden.json": {Data: []byte(`"ignored"`)},
|
||||
"broken/no.json": {Data: []byte(`"x"`)},
|
||||
}
|
||||
f, err := New(WithoutShippedData(), WithDataFS(fsys), WithSeed(1))
|
||||
f, err := New(WithDataFS(fsys), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatalf("New(WithDataFS) = %v", err)
|
||||
}
|
||||
@@ -109,13 +117,25 @@ func TestWithDataFS(t *testing.T) {
|
||||
t.Errorf("nested.x.y = %q, want z", got)
|
||||
}
|
||||
bad := fstest.MapFS{"broken.json": {Data: []byte(`{ not json`)}}
|
||||
if _, err := New(WithoutShippedData(), WithDataFS(bad)); err == nil || !strings.Contains(err.Error(), "broken.json") {
|
||||
if _, err := New(WithDataFS(bad)); err == nil || !strings.Contains(err.Error(), "broken.json") {
|
||||
t.Errorf("New(bad fs) = %v, want an error naming the file", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithDataFSLayersEachModuleOverTheOneBefore(t *testing.T) {
|
||||
a := fstest.MapFS{"x.json": {Data: []byte(`"a"`)}, "y.json": {Data: []byte(`"a"`)}}
|
||||
b := fstest.MapFS{"x.json": {Data: []byte(`"b"`)}}
|
||||
f, err := New(WithDataFS(a, b))
|
||||
if err != nil {
|
||||
t.Fatalf("New(WithDataFS(a, b)) = %v", err)
|
||||
}
|
||||
if got := fake(t, f, "x") + fake(t, f, "y"); got != "ba" {
|
||||
t.Errorf("x and y = %q, want b's x and a's y", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeIsSafeForConcurrentUse(t *testing.T) {
|
||||
f, err := New(WithSeed(1))
|
||||
f, err := New(withShipped(), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
+183
-49
@@ -4,7 +4,9 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"maps"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -16,11 +18,11 @@ import (
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"github.com/larvit/fejkdata/data"
|
||||
"github.com/larvit/fejkdata/data/sv_SE"
|
||||
"github.com/larvit/fejkdata/internal/datafiles"
|
||||
)
|
||||
|
||||
var shippedManifestFile = path.Join("data", datafiles.ManifestFile)
|
||||
|
||||
// withoutManifest is fsys with its manifest hidden, so it loads whole in New.
|
||||
type withoutManifest struct{ fs.FS }
|
||||
|
||||
@@ -31,15 +33,52 @@ func (w withoutManifest) Open(name string) (fs.File, error) {
|
||||
return w.FS.Open(name)
|
||||
}
|
||||
|
||||
// shippedModule is a module data.Modules lists, and its folder under data/, slash-separated.
|
||||
type shippedModule struct {
|
||||
fsys fs.FS
|
||||
dir string
|
||||
}
|
||||
|
||||
// importPath is the Go package the module's FS is exported from.
|
||||
func (m shippedModule) importPath() string { return "github.com/larvit/fejkdata/data/" + m.dir }
|
||||
|
||||
// holds reports whether the category at path is the module's: its tree spells its folder.
|
||||
func (m shippedModule) holds(path string) bool {
|
||||
return strings.HasPrefix(path, strings.ReplaceAll(m.dir, "/", ".")+".")
|
||||
}
|
||||
|
||||
// shippedModules is every module data.Modules lists, each folder found by descending its
|
||||
// tree while a folder holds one folder and nothing else.
|
||||
func shippedModules(t testing.TB) []shippedModule {
|
||||
t.Helper()
|
||||
var out []shippedModule
|
||||
for _, fsys := range data.Modules() {
|
||||
dir := "."
|
||||
for {
|
||||
entries, err := fs.ReadDir(fsys, dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
entries = slices.DeleteFunc(entries, func(e fs.DirEntry) bool { return strings.HasPrefix(e.Name(), ".") })
|
||||
if len(entries) != 1 || !entries[0].IsDir() {
|
||||
break
|
||||
}
|
||||
dir = path.Join(dir, entries[0].Name())
|
||||
}
|
||||
out = append(out, shippedModule{fsys: fsys, dir: dir})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// newShippedWhole loads every shipped category in New, as a source without an index 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)
|
||||
var modules []fs.FS
|
||||
for _, m := range shippedModules(t) {
|
||||
modules = append(modules, withoutManifest{m.fsys})
|
||||
}
|
||||
f, err := New(append([]Option{WithoutShippedData(), WithDataFS(withoutManifest{data})}, opts...)...)
|
||||
f, err := New(append([]Option{WithDataFS(modules...)}, opts...)...)
|
||||
if err != nil {
|
||||
t.Fatalf("New() = %v", err)
|
||||
}
|
||||
@@ -55,57 +94,150 @@ func loadedCategories(f *Generator) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// REGENERATE=1 rewrites the manifest.
|
||||
func TestShippedManifestIsCurrent(t *testing.T) {
|
||||
got, err := shippedManifest(newShippedWhole(t))
|
||||
// REGENERATE=1 rewrites the manifests.
|
||||
func TestShippedManifestsAreCurrent(t *testing.T) {
|
||||
modules := shippedModules(t)
|
||||
manifests, err := shippedManifests(newShippedWhole(t), modules)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if os.Getenv("REGENERATE") == "1" {
|
||||
if err := os.WriteFile(shippedManifestFile, got, 0o644); err != nil {
|
||||
for i, m := range modules {
|
||||
file := filepath.Join("data", filepath.FromSlash(m.dir), datafiles.ManifestFile)
|
||||
if os.Getenv("REGENERATE") == "1" {
|
||||
if err := os.WriteFile(file, manifests[i], 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
want, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
want, err := os.ReadFile(shippedManifestFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(got) != string(want) {
|
||||
t.Fatalf("%s is stale; regenerate it: docker compose run --rm --user \"$(id -u):$(id -g)\" generate", shippedManifestFile)
|
||||
if string(manifests[i]) != string(want) {
|
||||
t.Errorf("%s is stale; regenerate it: docker compose run --rm --user \"$(id -u):$(id -g)\" generate", file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shippedManifest is the manifest of whole's tree, one category a line.
|
||||
func shippedManifest(whole *Generator) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
b.WriteString("{\n\t\"index\": {")
|
||||
for i, s := range categorySites(&whole.root) {
|
||||
// shippedManifests is each module's manifest, read from whole's tree: its index, one category a
|
||||
// line, and every other module it reads, directly or through another.
|
||||
func shippedManifests(whole *Generator, modules []shippedModule) ([][]byte, error) {
|
||||
owner := func(path string) int {
|
||||
return slices.IndexFunc(modules, func(m shippedModule) bool { return m.holds(path) })
|
||||
}
|
||||
index := make([][]string, len(modules))
|
||||
direct := make([][]int, len(modules))
|
||||
for _, s := range categorySites(&whole.root) {
|
||||
i := owner(s.path)
|
||||
if i < 0 {
|
||||
return nil, fmt.Errorf("no shipped module holds %s", s.path)
|
||||
}
|
||||
line, err := json.Marshal(indexEntry(s))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if i > 0 {
|
||||
b.WriteString(",")
|
||||
}
|
||||
key, _ := json.Marshal(s.path)
|
||||
b.WriteString("\n\t\t" + string(key) + ": " + string(line))
|
||||
index[i] = append(index[i], "\n\t\t"+string(key)+": "+string(line))
|
||||
for _, c := range needs(&whole.root, s) {
|
||||
if j := owner(categoryPath(c.dir, c.name)); j != i && !slices.Contains(direct[i], j) {
|
||||
direct[i] = append(direct[i], j)
|
||||
}
|
||||
}
|
||||
}
|
||||
b.WriteString("\n\t}\n}\n")
|
||||
return b.Bytes(), nil
|
||||
out := make([][]byte, len(modules))
|
||||
for i := range modules {
|
||||
var b bytes.Buffer
|
||||
b.WriteString("{\n\t\"index\": {" + strings.Join(index[i], ",") + "\n\t}")
|
||||
if reads := readsOf(i, direct, modules); len(reads) > 0 {
|
||||
line, _ := json.Marshal(reads)
|
||||
b.WriteString(",\n\t\"reads\": " + string(line))
|
||||
}
|
||||
b.WriteString("\n}\n")
|
||||
out[i] = b.Bytes()
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// readsOf is every module module i reads, directly or through another, by import path.
|
||||
func readsOf(i int, direct [][]int, modules []shippedModule) []string {
|
||||
seen := map[int]bool{i: true}
|
||||
queue := slices.Clone(direct[i])
|
||||
var out []string
|
||||
for ; len(queue) > 0; queue = queue[1:] {
|
||||
j := queue[0]
|
||||
if seen[j] {
|
||||
continue
|
||||
}
|
||||
seen[j] = true
|
||||
out = append(out, modules[j].importPath())
|
||||
queue = append(queue, direct[j]...)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func shippedIndex(t *testing.T) map[string]datafiles.IndexEntry {
|
||||
t.Helper()
|
||||
m, err := shippedSource.Manifest()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
index := map[string]datafiles.IndexEntry{}
|
||||
for _, m := range shippedModules(t) {
|
||||
manifest, err := datafiles.FS(m.fsys, "").Manifest()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
maps.Copy(index, manifest.Index)
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
func TestEachShippedModuleNamesWhatItReads(t *testing.T) {
|
||||
for _, m := range shippedModules(t) {
|
||||
manifest, err := datafiles.FS(m.fsys, "").Manifest()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if m.dir == "sv_SE" && !slices.Contains(manifest.Reads, "github.com/larvit/fejkdata/data/geo/SE") {
|
||||
t.Errorf("sv_SE reads %v, want geo/SE among them", manifest.Reads)
|
||||
}
|
||||
if slices.Contains(manifest.Reads, m.importPath()) {
|
||||
t.Errorf("%s reads %v, want itself left out", m.dir, manifest.Reads)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAReadNoModuleProvidesNamesTheReadersModules(t *testing.T) {
|
||||
f, err := New(WithDataFS(sv_SE.FS))
|
||||
if err != nil {
|
||||
t.Fatalf("New(WithDataFS(sv_SE.FS)) = %v", err)
|
||||
}
|
||||
if _, err := f.Fake("sv_SE.address"); err == nil || !strings.Contains(err.Error(), "the module holding sv_SE.address reads by default github.com/larvit/fejkdata/data/geo/SE") {
|
||||
t.Errorf(`Fake("sv_SE.address") = %v, want the module it reads named`, err)
|
||||
}
|
||||
for name, opt := range map[string]Option{
|
||||
"no manifest": WithDataPath(writeData(t, map[string]string{"x": `"{/y}"`})),
|
||||
"no reads in one": WithDataFS(fstest.MapFS{".fejkdata.json": {Data: []byte(`{"index": {"x": {"paths": [""]}}}`)}, "x.json": {Data: []byte(`"{/y}"`)}}),
|
||||
} {
|
||||
f, err := New(opt)
|
||||
if err == nil {
|
||||
_, err = f.Fake("x")
|
||||
}
|
||||
if err == nil || !strings.Contains(err.Error(), "the module holding x names no module it reads by default") {
|
||||
t.Errorf("%s: x = %v, want it said that x's module names none", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestADataPathManifestNamesWhatItReads(t *testing.T) {
|
||||
dir := writeFiles(t, map[string]string{
|
||||
".fejkdata.json": `{"reads": ["example.com/places"]}`,
|
||||
"x.json": `"{/place}"`,
|
||||
})
|
||||
if _, err := New(WithDataPath(dir)); err == nil || !strings.Contains(err.Error(), "the module holding x reads by default example.com/places") {
|
||||
t.Errorf("New = %v, want the module x reads named", err)
|
||||
}
|
||||
return m.Index
|
||||
}
|
||||
|
||||
func TestNewLoadsNoShippedCategory(t *testing.T) {
|
||||
f, err := New()
|
||||
f, err := New(withShipped())
|
||||
if err != nil {
|
||||
t.Fatalf("New() = %v", err)
|
||||
}
|
||||
@@ -149,7 +281,7 @@ func TestReachingACategoryLoadsWhatItReads(t *testing.T) {
|
||||
absent: []string{"sv_SE.person"},
|
||||
},
|
||||
} {
|
||||
f, err := New()
|
||||
f, err := New(withShipped())
|
||||
if err != nil {
|
||||
t.Fatalf("New() = %v", err)
|
||||
}
|
||||
@@ -171,7 +303,7 @@ func TestReachingACategoryLoadsWhatItReads(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestADataPathLoadsWhatItReads(t *testing.T) {
|
||||
f, err := New(WithDataPath(writeData(t, map[string]string{"greeting": `"Hej {/sv_SE.person}!"`})))
|
||||
f, err := New(withShipped(), WithDataPath(writeData(t, map[string]string{"greeting": `"Hej {/sv_SE.person}!"`})))
|
||||
if err != nil {
|
||||
t.Fatalf("New() = %v", err)
|
||||
}
|
||||
@@ -196,7 +328,7 @@ func TestAReplacementBreakingAShippedReaderFailsItsFirstReach(t *testing.T) {
|
||||
"sv_SE/last-name.json": {Data: []byte(`"x"`)},
|
||||
}),
|
||||
} {
|
||||
f, err := New(opt)
|
||||
f, err := New(withShipped(), opt)
|
||||
if err != nil {
|
||||
t.Fatalf("New(%s) = %v", name, err)
|
||||
}
|
||||
@@ -216,7 +348,7 @@ func TestAnIndexedSourceLoadsOnFirstReachLikeTheShippedOne(t *testing.T) {
|
||||
"b.json": {Data: []byte(`"x"`)},
|
||||
"sub/c.json": {Data: []byte(`{"format":"{d}","d":"y"}`)},
|
||||
}
|
||||
f, err := New(WithoutShippedData(), WithDataFS(fsys), WithSeed(1))
|
||||
f, err := New(WithDataFS(fsys), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatalf("New() = %v", err)
|
||||
}
|
||||
@@ -235,7 +367,7 @@ func TestAnIndexedSourceLoadsOnFirstReachLikeTheShippedOne(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAnIndexedCategoryFailsAtFirstReachAndEveryReachAfter(t *testing.T) {
|
||||
f, err := New(WithoutShippedData(), WithDataFS(fstest.MapFS{
|
||||
f, err := New(WithDataFS(fstest.MapFS{
|
||||
".fejkdata.json": {Data: []byte(`{"index": {"bad": {"paths": [""]}, "good": {"paths": [""]}}}`)},
|
||||
"bad.json": {Data: []byte(`"{/nope}"`)},
|
||||
"good.json": {Data: []byte(`"fine"`)},
|
||||
@@ -273,8 +405,10 @@ func TestABrokenManifestFailsNewNamingEveryMistake(t *testing.T) {
|
||||
`{"index": {"a": {"paths": ["", "x", "x"]}}}`: {`paths item 3 repeats "x"`},
|
||||
`{"index": {"a": {"paths": [""], "parent": "b"}}}`: {`parent "b" names no entry`},
|
||||
`{"index": {"a": {"paths": [""], "reads": ["b"]}}}`: {`unknown key "reads"`},
|
||||
`{"reads": "b"}`: {"reads must be a list, not a string"},
|
||||
`{"reads": ["", 3]}`: {"reads item 1 is empty", "reads item 2 must be a string, not a number"},
|
||||
} {
|
||||
_, err := New(WithoutShippedData(), WithDataFS(fstest.MapFS{".fejkdata.json": {Data: []byte(manifest)}}))
|
||||
_, err := New(WithDataFS(fstest.MapFS{".fejkdata.json": {Data: []byte(manifest)}}))
|
||||
for _, w := range want {
|
||||
if err == nil || !strings.Contains(err.Error(), w) {
|
||||
t.Errorf("New(manifest %s) = %v, want an error naming %s", manifest, err, w)
|
||||
@@ -292,7 +426,7 @@ func TestNewMatchesErrLoadOnDataThatFailsToLoad(t *testing.T) {
|
||||
"a broken category": WithDataPath(writeData(t, map[string]string{"x": `{ not json`})),
|
||||
"a broken manifest": WithDataFS(fstest.MapFS{".fejkdata.json": {Data: []byte(`{`)}}),
|
||||
} {
|
||||
if _, err := New(opt); !errors.Is(err, ErrLoad) {
|
||||
if _, err := New(withShipped(), opt); !errors.Is(err, ErrLoad) {
|
||||
t.Errorf("New(%s) = %v, want it to match ErrLoad", name, err)
|
||||
}
|
||||
}
|
||||
@@ -301,14 +435,14 @@ func TestNewMatchesErrLoadOnDataThatFailsToLoad(t *testing.T) {
|
||||
func TestOnDemandRendersAsTheWholeLoad(t *testing.T) {
|
||||
whole := newShippedWhole(t)
|
||||
paths := whole.List()
|
||||
lazy, err := New()
|
||||
lazy, err := New(withShipped())
|
||||
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))
|
||||
a, err := New(withShipped(), WithSeed(7))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -326,7 +460,7 @@ func TestOnDemandRendersAsTheWholeLoad(t *testing.T) {
|
||||
|
||||
func TestEveryShippedCategoryLoadsAlone(t *testing.T) {
|
||||
for _, p := range sortedNames(shippedIndex(t)) {
|
||||
f, err := New()
|
||||
f, err := New(withShipped())
|
||||
if err != nil {
|
||||
t.Fatalf("New() = %v", err)
|
||||
}
|
||||
@@ -337,7 +471,7 @@ func TestEveryShippedCategoryLoadsAlone(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListRunsBesideAnOnDemandLoad(t *testing.T) {
|
||||
f, err := New()
|
||||
f, err := New(withShipped())
|
||||
if err != nil {
|
||||
t.Fatalf("New() = %v", err)
|
||||
}
|
||||
@@ -359,7 +493,7 @@ func TestListRunsBesideAnOnDemandLoad(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestATableWhoseEntryOmitsItsParentFailsAtFirstReach(t *testing.T) {
|
||||
f, err := New(WithoutShippedData(), WithDataFS(fstest.MapFS{
|
||||
f, err := New(WithDataFS(fstest.MapFS{
|
||||
".fejkdata.json": {Data: []byte(`{"index": {"city": {"paths": ["", "country", "name"]}, "country": {"paths": ["", "code", "name"]}}}`)},
|
||||
"country.json": {Data: []byte(`{"format":"{name}","rows":"country.tsv","key":"code"}`)},
|
||||
"country.tsv": {Data: []byte("code\tname\nSE\tSweden\n")},
|
||||
@@ -382,7 +516,7 @@ func TestATableWhoseEntryOmitsItsParentFailsAtFirstReach(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestADataPathTableUnderAShippedTableLoads(t *testing.T) {
|
||||
f, err := New(WithSeed(1), WithDataPath(writeFiles(t, map[string]string{
|
||||
f, err := New(withShipped(), WithSeed(1), WithDataPath(writeFiles(t, map[string]string{
|
||||
"sv_SE/nickname.json": `{"format":"{name}","rows":"nickname.tsv","key":"name","parent":"sex"}`,
|
||||
"sv_SE/nickname.tsv": "name\tsex\nKalle\tm\nLotta\tf\n",
|
||||
})))
|
||||
@@ -405,7 +539,7 @@ func indexEntry(s categorySite) datafiles.IndexEntry {
|
||||
}
|
||||
|
||||
func TestAnIndexedCategoryWithNoFileNamesTheManifest(t *testing.T) {
|
||||
f, err := New(WithoutShippedData(), WithDataFS(fstest.MapFS{
|
||||
f, err := New(WithDataFS(fstest.MapFS{
|
||||
".fejkdata.json": {Data: []byte(`{"index": {"gone": {"paths": [""]}}}`)},
|
||||
}))
|
||||
if err != nil {
|
||||
|
||||
+20
-20
@@ -165,7 +165,7 @@ func TestTableCellsAreStringNodes(t *testing.T) {
|
||||
"place.json": `{"format":"{name}","rows":"place.tsv"}`,
|
||||
"place.tsv": "name\tzip\nA\t{name}\nB\t2\n",
|
||||
})
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(bad)); err == nil || !strings.Contains(err.Error(), "place.tsv") || !strings.Contains(err.Error(), `no field "name"`) {
|
||||
if _, err := New(WithDataPath(bad)); err == nil || !strings.Contains(err.Error(), "place.tsv") || !strings.Contains(err.Error(), `no field "name"`) {
|
||||
t.Fatalf("New = %v, want a cell reading a column refused, naming the file", err)
|
||||
}
|
||||
}
|
||||
@@ -327,7 +327,7 @@ func TestTableStepUpRefusals(t *testing.T) {
|
||||
if _, err := f.Fake(path); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Fake(%q) = %v, want an error mentioning %s", path, err, want)
|
||||
}
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, with(geo(), map[string]string{
|
||||
_, err := New(WithDataPath(writeFiles(t, with(geo(), map[string]string{
|
||||
"x.json": `{"format":"{y}","y":"a"}`,
|
||||
"t.json": strconv.Quote("{/" + path + "}"),
|
||||
}))))
|
||||
@@ -335,7 +335,7 @@ func TestTableStepUpRefusals(t *testing.T) {
|
||||
t.Errorf("{/%s}: New = %v, want an error mentioning %s", path, err, want)
|
||||
}
|
||||
}
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, map[string]string{"s.json": `{"format":"{a..b}","a":{"format":"{b}","b":"x"}}`}))); err == nil || !strings.Contains(err.Error(), "only from a table's row") {
|
||||
if _, err := New(WithDataPath(writeFiles(t, map[string]string{"s.json": `{"format":"{a..b}","a":{"format":"{b}","b":"x"}}`}))); err == nil || !strings.Contains(err.Error(), "only from a table's row") {
|
||||
t.Errorf("{a..b} on a field: New = %v, want an error mentioning only from a table's row", err)
|
||||
}
|
||||
}
|
||||
@@ -464,7 +464,7 @@ func TestTableSelectorInAReference(t *testing.T) {
|
||||
"selector on template": {`"{/x[1].a}"`, "not a table"},
|
||||
"selector on repeating template": {`"{/rep[1]}"`, "not a table"},
|
||||
} {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, with(files, map[string]string{"bad.json": c.json, "rep.json": `{"format":"x","repeat":2}`}))))
|
||||
_, err := New(WithDataPath(writeFiles(t, with(files, map[string]string{"bad.json": c.json, "rep.json": `{"format":"x","repeat":2}`}))))
|
||||
if err == nil || !strings.Contains(err.Error(), c.want) {
|
||||
t.Errorf("%s: New = %v, want an error mentioning %q", name, err, c.want)
|
||||
}
|
||||
@@ -484,7 +484,7 @@ func TestTableReadsBesideEachOtherLoad(t *testing.T) {
|
||||
`"{/region[12].locality[L4].name} {/region[12].locality[L7].name}"`,
|
||||
`"{/region[14].name} {/locality[L4].name}"`,
|
||||
} {
|
||||
f, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, with(geo(), map[string]string{"x.json": json}))), WithSeed(1))
|
||||
f, err := New(WithDataPath(writeFiles(t, with(geo(), map[string]string{"x.json": json}))), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Errorf("%s: New = %v, want each read a draw of its own", json, err)
|
||||
continue
|
||||
@@ -504,7 +504,7 @@ func TestSelectedRowsAgreeAcrossSpellings(t *testing.T) {
|
||||
"a draw under the selection": `"{/region[12].name}|{/region[12].municipality.locality[L4].name}"`,
|
||||
}
|
||||
for name, json := range accepted {
|
||||
f, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, with(geo(), map[string]string{"x.json": json}))), WithSeed(1))
|
||||
f, err := New(WithDataPath(writeFiles(t, with(geo(), map[string]string{"x.json": json}))), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Errorf("%s: New = %v, want it accepted", name, err)
|
||||
continue
|
||||
@@ -595,7 +595,7 @@ func TestTableFences(t *testing.T) {
|
||||
"a category referencing itself": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv"}`, "t.tsv": "a\n{/t.a}\ny\n"}, "cycle"},
|
||||
}
|
||||
for name, c := range rejected {
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, c.files)))
|
||||
_, err := New(WithDataPath(writeFiles(t, c.files)))
|
||||
if err == nil {
|
||||
t.Errorf("%s: New = nil error, want it rejected at load", name)
|
||||
continue
|
||||
@@ -616,7 +616,7 @@ func TestTableFences(t *testing.T) {
|
||||
"a cell repeating a reference": with(base, map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv"}`, "t.tsv": "a\nx\n{/region} {/region} {uppercase(/region)}\n"}),
|
||||
}
|
||||
for name, files := range accepted {
|
||||
f, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, files)), WithSeed(1))
|
||||
f, err := New(WithDataPath(writeFiles(t, files)), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Errorf("%s: New = %v, want each read a draw of its own", name, err)
|
||||
continue
|
||||
@@ -627,7 +627,7 @@ func TestTableFences(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, err := New(WithoutShippedData(), WithDataFS(os.DirFS(writeFiles(t, base)))); err != nil {
|
||||
if _, err := New(WithDataFS(os.DirFS(writeFiles(t, base)))); err != nil {
|
||||
t.Fatalf("New(WithDataFS) = %v, want a table loaded from any fs.FS", err)
|
||||
}
|
||||
inline := newGenerator(t, writeFiles(t, base))
|
||||
@@ -711,7 +711,7 @@ func TestTableRowsAreAlternatives(t *testing.T) {
|
||||
}
|
||||
symbols := regexp.MustCompile(`€|kr|\$`)
|
||||
for name, more := range accepted {
|
||||
g, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, with(files, more))), WithSeed(1))
|
||||
g, err := New(WithDataPath(writeFiles(t, with(files, more))), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Errorf("%s: New = %v, want it accepted", name, err)
|
||||
continue
|
||||
@@ -743,7 +743,7 @@ func TestTableRowsAreAlternatives(t *testing.T) {
|
||||
"a cell reaching another's cells": {"y.json": `{"format":"{sym}","rows":"y.tsv","key":"k"}`, "y.tsv": "k\tsym\n1\t{/cur[SEK].sym}\n2\t{/cur[USD].sym}\n", "cur.tsv": "code\tsym\nEUR\t€\nSEK\tkr\nUSD\t$\n", "country.tsv": "alpha2\tname\tmoney\nFI\tFinland\t{/y[1].sym} {/cur[EUR].sym}\nSE\tSweden\t{/cur[SEK].sym}\n"},
|
||||
}
|
||||
for name, more := range twoRows {
|
||||
g, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, with(files, more))), WithSeed(1))
|
||||
g, err := New(WithDataPath(writeFiles(t, with(files, more))), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Errorf("%s: New = %v, want each read a draw of its own", name, err)
|
||||
continue
|
||||
@@ -765,7 +765,7 @@ func TestTableOverridesByLayering(t *testing.T) {
|
||||
"misc/language.json": `{"format":"{name}","rows":"language.tsv","key":"code"}`,
|
||||
"misc/language.tsv": "code\tname\nxx\tNowhere\nyy\tElsewhere\n",
|
||||
})
|
||||
f, err := New(WithDataPath(mine), WithSeed(1))
|
||||
f, err := New(withShipped(), WithDataPath(mine), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -778,7 +778,7 @@ func TestTableOverridesByLayering(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestShippedTables(t *testing.T) {
|
||||
f := newGenerator(t, "data/misc", WithSeed(1))
|
||||
f := newGenerator(t, "data/misc/misc", WithSeed(1))
|
||||
for path, want := range map[string]string{
|
||||
"territory[SE]": "Sweden",
|
||||
"territory[Sweden].alpha3": "SWE",
|
||||
@@ -893,7 +893,7 @@ func TestShippedTables(t *testing.T) {
|
||||
Method string `fake:"{m.method}"`
|
||||
Safe bool `fake:"{m.safe}"`
|
||||
}
|
||||
if err := newGenerator(t, "data", WithSeed(1)).FakeStruct(&row); err != nil {
|
||||
if err := shipped(t, WithSeed(1)).FakeStruct(&row); err != nil {
|
||||
t.Fatalf("FakeStruct into a bool = %v, want the register's yes and no shipped as true and false", err)
|
||||
}
|
||||
want, named := map[string][2]bool{
|
||||
@@ -992,7 +992,7 @@ func TestEveryTldRowSpellsItsKey(t *testing.T) {
|
||||
// columns asked for and that every row fills them.
|
||||
func shippedRows(t *testing.T, file string, want ...string) (map[string]int, [][]string) {
|
||||
t.Helper()
|
||||
data, err := os.ReadFile(filepath.Join("data", "misc", file))
|
||||
data, err := os.ReadFile(filepath.Join("data", "misc", "misc", file))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1122,7 +1122,7 @@ func TestCellReadsLoadAndRender(t *testing.T) {
|
||||
"region.tsv": "code\tname\tpopulation\tnote\n01\tStockholms län\t2400000\t{/addr}\n12\tSkåne län\t1400000\t-\n14\tVästra Götalands län\t1750000\t-\n",
|
||||
})
|
||||
for _, x := range []string{`"{/region.note} {/municipality.note}"`, `"{/region}|{/municipality}"`} {
|
||||
f, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, with(cells, map[string]string{"x.json": x}))), WithSeed(1))
|
||||
f, err := New(WithDataPath(writeFiles(t, with(cells, map[string]string{"x.json": x}))), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatalf("New(%s) = %v, want the cells accepted", x, err)
|
||||
}
|
||||
@@ -1136,7 +1136,7 @@ func TestDatatypeRefusesARowRead(t *testing.T) {
|
||||
for _, format := range []string{"{/region}", "{/region[01]}"} {
|
||||
files := with(geo(), map[string]string{"row.json": `{"format":"","col":{"format":"` + format + `","datatype":"integer"}}`})
|
||||
want := `"{name}" renders a row of region, which is composed text`
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) {
|
||||
if _, err := New(WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("%s: New = %v, want an error containing %q", format, err, want)
|
||||
}
|
||||
}
|
||||
@@ -1148,12 +1148,12 @@ func TestATableErrorNamesItsPathAndACellItsLine(t *testing.T) {
|
||||
"g/t.tsv": "a\tb\nx\t-\ny\t{/nope}\n",
|
||||
}
|
||||
want := "g.t.b, line 3: reference {/nope}"
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) {
|
||||
if _, err := New(WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("a cell's bad reference: New = %v, want it to contain %q", err, want)
|
||||
}
|
||||
files["g/t.json"], files["g/t.tsv"] = `{"format":"{a} {/nope}","rows":"t.tsv","key":"a"}`, "a\tb\nx\t-\ny\t-\n"
|
||||
want = "g.t: reference {/nope}"
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) {
|
||||
if _, err := New(WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("a format's bad reference: New = %v, want it to contain %q", err, want)
|
||||
}
|
||||
f := newGenerator(t, writeFiles(t, geo()), WithSeed(1))
|
||||
@@ -1165,7 +1165,7 @@ func TestATableErrorNamesItsPathAndACellItsLine(t *testing.T) {
|
||||
t.Errorf("Fake(%s) = %v, want it to contain %q", path, err, want)
|
||||
}
|
||||
}
|
||||
shipped, err := New(WithSeed(1))
|
||||
shipped, err := New(withShipped(), WithSeed(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user