diff --git a/calc_test.go b/calc_test.go index ada0eaa..0de014a 100644 --- a/calc_test.go +++ b/calc_test.go @@ -310,7 +310,7 @@ func TestCalcRefusesABadOperand(t *testing.T) { } func TestATypedColumnRefusesACalcOverNoDigits(t *testing.T) { - _, err := New(WithDataPath(writeData(t, map[string]string{ + _, err := New(WithSeed(1), 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") { diff --git a/cli/cli_test.go b/cli/cli_test.go index 255236f..5149764 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -127,6 +127,13 @@ func TestRunDotPath(t *testing.T) { } } +func TestRunWithoutASeedSeedsFromTheSystem(t *testing.T) { + _, a, errb := runOut("{uuid()}") + if code, b, _ := runOut("{uuid()}"); code != 0 || a == "" || a == b { + t.Fatalf("two runs without --seed = %q and %q, exit %d, stderr=%q; want each its own draw", a, b, code, errb) + } +} + func TestRunSeedSpellings(t *testing.T) { _, want, _ := runOut("{/address}", "--seed", "42", "--data-path", svSE) for _, args := range [][]string{ diff --git a/data/en_US/fs_test.go b/data/en_US/fs_test.go index 22b0c96..8ffccc5 100644 --- a/data/en_US/fs_test.go +++ b/data/en_US/fs_test.go @@ -12,7 +12,7 @@ func TestFSLoadsByItsManifest(t *testing.T) { if _, err := fs.Stat(enus.FS, ".fejkdata.json"); err != nil { t.Fatalf("FS holds no manifest: %v", err) } - f, err := fejkdata.New(fejkdata.WithDataFS(enus.Modules()...)) + f, err := fejkdata.New(fejkdata.WithDataFS(enus.Modules()...), fejkdata.WithSeed(1)) if err != nil { t.Fatalf("New(WithDataFS(Modules()...)) = %v", err) } diff --git a/data/geo/SE/fs_test.go b/data/geo/SE/fs_test.go index 1ce1e51..37b90f0 100644 --- a/data/geo/SE/fs_test.go +++ b/data/geo/SE/fs_test.go @@ -12,7 +12,7 @@ func TestFSLoadsByItsManifest(t *testing.T) { if _, err := fs.Stat(geose.FS, ".fejkdata.json"); err != nil { t.Fatalf("FS holds no manifest: %v", err) } - f, err := fejkdata.New(fejkdata.WithDataFS(geose.Modules()...)) + f, err := fejkdata.New(fejkdata.WithDataFS(geose.Modules()...), fejkdata.WithSeed(1)) if err != nil { t.Fatalf("New(WithDataFS(Modules()...)) = %v", err) } diff --git a/data/geo/US/fs_test.go b/data/geo/US/fs_test.go index 3c1b3c7..1e26954 100644 --- a/data/geo/US/fs_test.go +++ b/data/geo/US/fs_test.go @@ -12,7 +12,7 @@ func TestFSLoadsByItsManifest(t *testing.T) { if _, err := fs.Stat(geous.FS, ".fejkdata.json"); err != nil { t.Fatalf("FS holds no manifest: %v", err) } - f, err := fejkdata.New(fejkdata.WithDataFS(geous.Modules()...)) + f, err := fejkdata.New(fejkdata.WithDataFS(geous.Modules()...), fejkdata.WithSeed(1)) if err != nil { t.Fatalf("New(WithDataFS(Modules()...)) = %v", err) } diff --git a/data/misc/fs_test.go b/data/misc/fs_test.go index bdecd2a..8ef6afd 100644 --- a/data/misc/fs_test.go +++ b/data/misc/fs_test.go @@ -12,7 +12,7 @@ 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.Modules()...)) + f, err := fejkdata.New(fejkdata.WithDataFS(misc.Modules()...), fejkdata.WithSeed(1)) if err != nil { t.Fatalf("New(WithDataFS(Modules()...)) = %v", err) } diff --git a/data/sv_SE/fs_test.go b/data/sv_SE/fs_test.go index 78e1199..da4a31c 100644 --- a/data/sv_SE/fs_test.go +++ b/data/sv_SE/fs_test.go @@ -12,7 +12,7 @@ func TestFSLoadsByItsManifest(t *testing.T) { if _, err := fs.Stat(svse.FS, ".fejkdata.json"); err != nil { t.Fatalf("FS holds no manifest: %v", err) } - f, err := fejkdata.New(fejkdata.WithDataFS(svse.Modules()...)) + f, err := fejkdata.New(fejkdata.WithDataFS(svse.Modules()...), fejkdata.WithSeed(1)) if err != nil { t.Fatalf("New(WithDataFS(Modules()...)) = %v", err) } diff --git a/datatype_test.go b/datatype_test.go index c385bb9..62567cf 100644 --- a/datatype_test.go +++ b/datatype_test.go @@ -88,7 +88,7 @@ func TestDatatypeRejectsAValueItsTypeRejects(t *testing.T) { for name, body := range tree { files[name] = body } - _, err := New(WithDataPath(writeData(t, files))) + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{"row": row}))); err == nil || !strings.HasSuffix(err.Error(), want) { + if _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{ + _, err := New(WithSeed(1), WithDataPath(writeData(t, map[string]string{ "a": `{"format":"","c":["{/b.x}",{"format":"1","datatype":"integer"}]}`, "b": `{"format":"","x":["2",{"format":"1","datatype":"integer"}]}`, }))) diff --git a/fejkdata_test.go b/fejkdata_test.go index 3c2751a..2d717db 100644 --- a/fejkdata_test.go +++ b/fejkdata_test.go @@ -7,7 +7,6 @@ import ( "path/filepath" "strings" "testing" - "testing/fstest" "github.com/larvit/fejkdata/data" "github.com/larvit/fejkdata/internal/drawstate" @@ -30,7 +29,7 @@ func newGeneratorN(t *testing.T, dirs []string, opts ...Option) *Generator { for _, dir := range dirs { all = append(all, WithDataPath(dir)) } - f, err := New(append(all, opts...)...) + f, err := New(append(append(all, WithSeed(1)), opts...)...) if err != nil { t.Fatalf("New(%q): %v", dirs, err) } @@ -41,7 +40,7 @@ func newGeneratorN(t *testing.T, dirs []string, opts ...Option) *Generator { // categories sit at the root, failing on error. func newBareModule(t *testing.T, module string, opts ...Option) *Generator { t.Helper() - f, err := New(append([]Option{WithDataFS(withoutManifest{os.DirFS(filepath.Join("data", module))})}, opts...)...) + f, err := New(append([]Option{WithDataFS(withoutManifest{os.DirFS(filepath.Join("data", module))}), WithSeed(1)}, opts...)...) if err != nil { t.Fatalf("New(%s without its manifest): %v", module, err) } @@ -59,26 +58,24 @@ func fake(t *testing.T, f *Generator, path string) string { } func TestNewMissingDirectory(t *testing.T) { - _, err := New(WithDataPath("data/de_DE")) + _, err := New(WithSeed(1), 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(WithDataPath("")) + _, err := New(WithSeed(1), WithDataPath("")) if err == nil || !strings.Contains(err.Error(), "empty") { t.Fatalf("New(WithDataPath(\"\")) = %v, want the empty path named", err) } } -func TestNewReportsAnEntropyFailure(t *testing.T) { - saved := randomBytes - randomBytes = func([]byte) (int, error) { return 0, errors.New("no entropy") } - defer func() { randomBytes = saved }() - _, 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 TestNewWithoutASeedFailsNamingWithSeed(t *testing.T) { + for name, opts := range map[string][]Option{"no option": nil, "only data": {WithDataFS()}} { + if _, err := New(opts...); err == nil || !strings.Contains(err.Error(), "WithSeed(") { + t.Errorf("New(%s) = %v, want it refused, naming WithSeed", name, err) + } } } diff --git a/loading_test.go b/loading_test.go index e7fddb8..c37c90e 100644 --- a/loading_test.go +++ b/loading_test.go @@ -187,12 +187,12 @@ func TestRepeatProductAlongAPathIsCapped(t *testing.T) { "b": `{"format":"x","repeat":2048}`, }, } { - _, err := New(WithDataPath(writeData(t, files))) + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{ + if _, err := New(WithSeed(1), 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(WithDataPath(file)); err == nil || !strings.Contains(err.Error(), "xx_XX: not a directory") { + if _, err := New(WithSeed(1), WithDataPath(file)); err == nil || !strings.Contains(err.Error(), "xx_XX: not a directory") { t.Errorf("New(file) = %v, want not-a-directory error", err) } // Invalid JSON in a category file fails. - if _, err := New(WithDataPath(writeData(t, map[string]string{"broken": `{ not json`}))); err == nil { + if _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, c.files))) + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, c.files))) + f, err := New(WithSeed(1), WithDataPath(writeData(t, c.files))) if err != nil { t.Errorf("%s: New = %v, want it accepted", name, err) continue diff --git a/name_test.go b/name_test.go index dc8827d..44d5ccd 100644 --- a/name_test.go +++ b/name_test.go @@ -253,7 +253,7 @@ func TestNameErrors(t *testing.T) { } files[name] = body } - _, err := New(WithDataPath(writeFiles(t, files))) + _, err := New(WithSeed(1), 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(WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) { + if _, err := New(WithSeed(1), 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) } } diff --git a/node_test.go b/node_test.go index c899efb..7117327 100644 --- a/node_test.go +++ b/node_test.go @@ -17,7 +17,7 @@ func TestAnOptionOfTheWrongKindIsNamedByItsJSONKind(t *testing.T) { t.Errorf("compile(%s) = %v, want %q", src, err, want) } } - _, err := New(WithDataPath(writeFiles(t, map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","key":5}`, "t.tsv": "a\nx\n"}))) + _, err := New(WithSeed(1), 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) } diff --git a/path_test.go b/path_test.go index 7983033..3ae776c 100644 --- a/path_test.go +++ b/path_test.go @@ -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(WithDataPath(dir)) + _, err := New(WithSeed(1), WithDataPath(dir)) if err == nil || !strings.Contains(err.Error(), `field "a.b" contains "."`) { t.Fatalf("New = %v, want the dotted field name rejected", err) } diff --git a/perf_test.go b/perf_test.go index ef47b62..f9f4644 100644 --- a/perf_test.go +++ b/perf_test.go @@ -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(WithDataFS(fstest.MapFS{"x.json": {Data: []byte(s.json)}})) + f, err := New(WithSeed(1), 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(WithDataFS(fstest.MapFS{ + f, err := New(WithSeed(1), 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(WithDataFS(fstest.MapFS{ + f, err := New(WithSeed(1), 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(WithDataFS(deep)) + f, err = New(WithSeed(1), 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(WithDataFS(fstest.MapFS{"x.json": {Data: []byte(s.json)}})) + f, err := New(WithSeed(1), 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(WithDataFS(fstest.MapFS{"x.json": {Data: []byte(`{"format":"","a":"x","b":"y","c":"z"}`)}})) + f, err := New(WithSeed(1), WithDataFS(fstest.MapFS{"x.json": {Data: []byte(`{"format":"","a":"x","b":"y","c":"z"}`)}})) if err != nil { t.Fatal(err) } diff --git a/pick_test.go b/pick_test.go index f9f5350..9cdb113 100644 --- a/pick_test.go +++ b/pick_test.go @@ -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(WithDataPath(writeData(t, map[string]string{ + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{ + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{ + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, files))); err != nil { + if _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{ + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{"cat": cat}))) + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{ + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{"cat": c.file}))) + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{"cat": file}))) + _, err := New(WithSeed(1), 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) } diff --git a/reference_test.go b/reference_test.go index 5c6b392..62b82c1 100644 --- a/reference_test.go +++ b/reference_test.go @@ -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(WithDataPath(writeData(t, files))); err == nil { + if _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{"cat": file}))) + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{ + if _, err := New(WithSeed(1), 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(WithDataPath(dir)) + _, err := New(WithSeed(1), WithDataPath(dir)) if err == nil { t.Fatalf("%s: New = nil error, want a load error", name) } @@ -210,7 +210,7 @@ func TestNewErrorPathIsCanonical(t *testing.T) { }, } for _, c := range cases { - _, err := New(WithDataPath(writeData(t, c.files))) + _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, map[string]string{ + _, err := New(WithSeed(1), WithDataPath(writeData(t, map[string]string{ "who": `[{"format":"{f}{h}","f":"1","h":"x"},{"format":"{g}{h}","g":"2","h":"y"}]`, "card": `"{/who.f}"`, }))) @@ -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(WithDataPath(writeData(t, files))); err == nil { + if _, err := New(WithSeed(1), 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(WithDataPath(writeData(t, files))) + _, err := New(WithSeed(1), 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(WithDataPath(writeFiles(t, map[string]string{ + _, err := New(WithSeed(1), 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]}"`, diff --git a/shipped_data_test.go b/shipped_data_test.go index 3eef3bf..ac6c9a8 100644 --- a/shipped_data_test.go +++ b/shipped_data_test.go @@ -95,7 +95,7 @@ func TestAGeneratorWithNoDataRendersWhatReadsNone(t *testing.T) { "no module": {WithDataFS()}, "empty folder": {WithDataPath(t.TempDir())}, } { - f, err := New(opts...) + f, err := New(append(opts, WithSeed(1))...) if err != nil { t.Fatalf("%s: New = %v, want a generator holding no data", name, err) } @@ -146,7 +146,7 @@ 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(WithDataFS(bad)); err == nil || !strings.Contains(err.Error(), "broken.json") { + if _, err := New(WithSeed(1), WithDataFS(bad)); err == nil || !strings.Contains(err.Error(), "broken.json") { t.Errorf("New(bad fs) = %v, want an error naming the file", err) } } @@ -154,7 +154,7 @@ func TestWithDataFS(t *testing.T) { 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)) + f, err := New(WithSeed(1), WithDataFS(a, b)) if err != nil { t.Fatalf("New(WithDataFS(a, b)) = %v", err) } @@ -165,7 +165,7 @@ func TestWithDataFSLayersEachModuleOverTheOneBefore(t *testing.T) { func TestWithDataFSRefusesANilModule(t *testing.T) { var unset fs.FS - if _, err := New(WithDataFS(fstest.MapFS{}, unset)); err == nil || !strings.Contains(err.Error(), "WithDataFS module 2 is nil") { + if _, err := New(WithSeed(1), WithDataFS(fstest.MapFS{}, unset)); err == nil || !strings.Contains(err.Error(), "WithDataFS module 2 is nil") { t.Errorf("New(WithDataFS(fsys, nil)) = %v, want module 2 named nil", err) } } diff --git a/shipped_test.go b/shipped_test.go index d7a0393..2f9f5bd 100644 --- a/shipped_test.go +++ b/shipped_test.go @@ -97,7 +97,7 @@ func newShippedWhole(t testing.TB, opts ...Option) *Generator { for _, m := range shippedModules(t) { modules = append(modules, m.whole(t)) } - f, err := New(append([]Option{WithDataFS(modules...)}, opts...)...) + f, err := New(append([]Option{WithDataFS(modules...), WithSeed(1)}, opts...)...) if err != nil { t.Fatalf("New() = %v", err) } @@ -223,7 +223,7 @@ func TestEachShippedPackagesModulesHoldWhatItReadsAndRender(t *testing.T) { if err != nil { t.Fatal(err) } - f, err := New(WithDataFS(set...)) + f, err := New(WithSeed(1), WithDataFS(set...)) if err != nil { t.Fatalf("New(%s's Modules) = %v", m.namespace, err) } @@ -236,7 +236,7 @@ func TestEachShippedPackagesModulesHoldWhatItReadsAndRender(t *testing.T) { } func TestALocaleAloneFailsAtItsAddressNamingTheMissingGeo(t *testing.T) { - f, err := New(WithDataFS(svse.FS)) + f, err := New(WithSeed(1), WithDataFS(svse.FS)) if err != nil { t.Fatalf("New(WithDataFS(svse.FS)) = %v", err) } @@ -249,7 +249,7 @@ func TestALocaleAloneFailsAtItsAddressNamingTheMissingGeo(t *testing.T) { } func TestAMountedModuleNamesItsRootFolderByItsNamespace(t *testing.T) { - _, err := New(WithDataFS(fstest.MapFS{ + _, err := New(WithSeed(1), WithDataFS(fstest.MapFS{ ".fejkdata.json": {Data: []byte(`{"namespace": "my.data"}`)}, "t.json": {Data: []byte(`{"format":"{name}","rows":"t.tsv","key":"name"}`)}, })) @@ -259,7 +259,7 @@ func TestAMountedModuleNamesItsRootFolderByItsNamespace(t *testing.T) { } func TestAMountedModuleNamesItsFilesByItsNamespace(t *testing.T) { - _, err := New(WithDataFS(fstest.MapFS{ + _, err := New(WithSeed(1), WithDataFS(fstest.MapFS{ ".fejkdata.json": {Data: []byte(`{"namespace": "my.data"}`)}, "word.json": {Data: []byte(`{`)}, })) @@ -281,7 +281,7 @@ func TestANamespaceMountsAModuleUnderIt(t *testing.T) { "sub/word.json": {Data: []byte(`"hej"`)}, }), } { - f, err := New(opt) + f, err := New(opt, WithSeed(1)) if err != nil { t.Fatalf("New(%s) = %v", name, err) } @@ -294,7 +294,7 @@ func TestANamespaceMountsAModuleUnderIt(t *testing.T) { } func TestNewLoadsNoShippedCategory(t *testing.T) { - f, err := New(withShipped()) + f, err := New(withShipped(), WithSeed(1)) if err != nil { t.Fatalf("New() = %v", err) } @@ -338,7 +338,7 @@ func TestReachingACategoryLoadsWhatItReads(t *testing.T) { absent: []string{"sv_SE.person"}, }, } { - f, err := New(withShipped()) + f, err := New(withShipped(), WithSeed(1)) if err != nil { t.Fatalf("New() = %v", err) } @@ -360,7 +360,7 @@ func TestReachingACategoryLoadsWhatItReads(t *testing.T) { } func TestADataPathLoadsWhatItReads(t *testing.T) { - f, err := New(withShipped(), WithDataPath(writeData(t, map[string]string{"greeting": `"Hej {/sv_SE.person}!"`}))) + f, err := New(withShipped(), WithSeed(1), WithDataPath(writeData(t, map[string]string{"greeting": `"Hej {/sv_SE.person}!"`}))) if err != nil { t.Fatalf("New() = %v", err) } @@ -385,7 +385,7 @@ func TestAReplacementBreakingAShippedReaderFailsItsFirstReach(t *testing.T) { "sv_SE/last-name.json": {Data: []byte(`"x"`)}, }), } { - f, err := New(withShipped(), opt) + f, err := New(withShipped(), WithSeed(1), opt) if err != nil { t.Fatalf("New(%s) = %v", name, err) } @@ -424,7 +424,7 @@ func TestAnIndexedSourceLoadsOnFirstReachLikeTheShippedOne(t *testing.T) { } func TestAnIndexedCategoryFailsAtFirstReachAndEveryReachAfter(t *testing.T) { - f, err := New(WithDataFS(fstest.MapFS{ + f, err := New(WithSeed(1), WithDataFS(fstest.MapFS{ ".fejkdata.json": {Data: []byte(`{"index": {"bad": {"paths": [""]}, "good": {"paths": [""]}}}`)}, "bad.json": {Data: []byte(`"{/nope}"`)}, "good.json": {Data: []byte(`"fine"`)}, @@ -467,7 +467,7 @@ func TestABrokenManifestFailsNewNamingEveryMistake(t *testing.T) { `{"namespace": "a..b"}`: {`namespace "a..b"`}, `{"index": {"b.x": {"paths": [""]}}, "namespace": "a"}`: {`index entry "b.x" lies outside the namespace "a"`}, } { - _, err := New(WithDataFS(fstest.MapFS{".fejkdata.json": {Data: []byte(manifest)}})) + _, err := New(WithSeed(1), 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) @@ -485,7 +485,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(withShipped(), opt); !errors.Is(err, ErrLoad) { + if _, err := New(withShipped(), WithSeed(1), opt); !errors.Is(err, ErrLoad) { t.Errorf("New(%s) = %v, want it to match ErrLoad", name, err) } } @@ -494,7 +494,7 @@ func TestNewMatchesErrLoadOnDataThatFailsToLoad(t *testing.T) { func TestOnDemandRendersAsTheWholeLoad(t *testing.T) { whole := newShippedWhole(t) paths := whole.List() - lazy, err := New(withShipped()) + lazy, err := New(withShipped(), WithSeed(1)) if err != nil { t.Fatalf("New() = %v", err) } @@ -519,7 +519,7 @@ func TestOnDemandRendersAsTheWholeLoad(t *testing.T) { func TestEveryShippedCategoryLoadsAlone(t *testing.T) { for _, p := range sortedNames(shippedIndex(t)) { - f, err := New(withShipped()) + f, err := New(withShipped(), WithSeed(1)) if err != nil { t.Fatalf("New() = %v", err) } @@ -530,7 +530,7 @@ func TestEveryShippedCategoryLoadsAlone(t *testing.T) { } func TestListRunsBesideAnOnDemandLoad(t *testing.T) { - f, err := New(withShipped()) + f, err := New(withShipped(), WithSeed(1)) if err != nil { t.Fatalf("New() = %v", err) } @@ -552,7 +552,7 @@ func TestListRunsBesideAnOnDemandLoad(t *testing.T) { } func TestATableWhoseEntryOmitsItsParentFailsAtFirstReach(t *testing.T) { - f, err := New(WithDataFS(fstest.MapFS{ + f, err := New(WithSeed(1), 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")}, @@ -598,7 +598,7 @@ func indexEntry(s categorySite) datafiles.IndexEntry { } func TestAMountedIndexedCategoryWithNoFileNamesTheManifestBesideItsFiles(t *testing.T) { - f, err := New(WithDataFS(fstest.MapFS{ + f, err := New(WithSeed(1), WithDataFS(fstest.MapFS{ ".fejkdata.json": {Data: []byte(`{"index": {"my.gone": {"paths": [""]}}, "namespace": "my"}`)}, })) if err != nil { @@ -610,7 +610,7 @@ func TestAMountedIndexedCategoryWithNoFileNamesTheManifestBesideItsFiles(t *test } func TestAnIndexedCategoryWithNoFileNamesTheManifest(t *testing.T) { - f, err := New(WithDataFS(fstest.MapFS{ + f, err := New(WithSeed(1), WithDataFS(fstest.MapFS{ ".fejkdata.json": {Data: []byte(`{"index": {"gone": {"paths": [""]}}}`)}, })) if err != nil { diff --git a/table_test.go b/table_test.go index 8be4f86..da02254 100644 --- a/table_test.go +++ b/table_test.go @@ -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(WithDataPath(bad)); err == nil || !strings.Contains(err.Error(), "place.tsv") || !strings.Contains(err.Error(), `no field "name"`) { + if _, err := New(WithSeed(1), 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(WithDataPath(writeFiles(t, with(geo(), map[string]string{ + _, err := New(WithSeed(1), 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(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(WithSeed(1), 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(WithDataPath(writeFiles(t, with(files, map[string]string{"bad.json": c.json, "rep.json": `{"format":"x","repeat":2}`})))) + _, err := New(WithSeed(1), 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) } @@ -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(WithDataPath(writeFiles(t, c.files))) + _, err := New(WithSeed(1), WithDataPath(writeFiles(t, c.files))) if err == nil { t.Errorf("%s: New = nil error, want it rejected at load", name) continue @@ -627,7 +627,7 @@ func TestTableFences(t *testing.T) { } } } - if _, err := New(WithDataFS(os.DirFS(writeFiles(t, base)))); err != nil { + if _, err := New(WithSeed(1), 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)) @@ -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(WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) { + if _, err := New(WithSeed(1), 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(WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) { + if _, err := New(WithSeed(1), 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(WithDataPath(writeFiles(t, files))); err == nil || !strings.Contains(err.Error(), want) { + if _, err := New(WithSeed(1), 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))