Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33

Open
lilleman wants to merge 966 commits from worktree-todo-relative-dates-exact-counts into main
9 changed files with 48 additions and 22 deletions
Showing only changes of commit 1e159fdf3a - Show all commits
+1 -4
View File
@@ -203,7 +203,7 @@ func mustPanic(t *testing.T, name string, call func()) {
}
func TestClassBuiltinArgs(t *testing.T) {
for _, bad := range []string{`"{digits(0)}"`, `"{digits(2000000000)}"`, `"{upper(-1)}"`, `"{lower(x)}"`, `"{digits()}"`, `"{upper(1,2)}"`} {
for _, bad := range []string{`"{digits(2000000000)}"`, `"{upper(-1)}"`, `"{lower(x)}"`, `"{digits()}"`, `"{upper(1,2)}"`} {
if _, err := resolved(t, bad); err == nil {
t.Errorf("compile(%s) = nil error, want the arg rejected", bad)
}
@@ -379,13 +379,10 @@ func TestFunctionTokenLuhn(t *testing.T) {
func TestBuiltinCompileErrors(t *testing.T) {
for _, bad := range []string{
`"{digits(0)}"`, // count must be positive
`"{upper(x)}"`, // count must be an integer
`"{int(a,b)}"`, // non-integer args
`"{int(5,1)}"`, // min > max
`"{hex(0)}"`, // count must be positive
`"{nanoid(-1)}"`, // negative count
`"{base64(0)}"`, // count must be positive
`"{float(1,2,-1)}"`, // negative decimals
`"{float(NaN,NaN,2)}"`, // bounds must be finite
`"{float(Inf,Inf,2)}"`, // same-sign infinities
+6
View File
@@ -333,3 +333,9 @@ func TestATypedColumnProvesACalcOverAName(t *testing.T) {
t.Fatalf("New = %v, want the typed column refused over an operand that is not always a number", err)
}
}
func TestARepeatOfZeroIsNoConstant(t *testing.T) {
if got := mustRender(t, engine(1), `{"format":"{calc(x / y)}","x":"1","y":{"format":"0","repeat":0}}`); got != "NaN" {
t.Errorf("a divisor rendering nothing = %q, want NaN", got)
}
}
+5 -3
View File
@@ -516,9 +516,11 @@ func TestRunNoShippedData(t *testing.T) {
if code != 0 || strings.TrimSpace(out) == "" {
t.Errorf("run = %d, out=%q", code, out)
}
code, _, errb = runOut("{/sv_SE.person}", "--no-shipped-data")
if code != 2 || !strings.Contains(errb, "--no-shipped-data needs at least one --data-path") {
t.Errorf("--no-shipped-data alone = %d, %q, want misuse naming --data-path", code, errb)
if code, out, errb := runOut("{digits(3)}", "--no-shipped-data"); code != 0 || len(out) != 3 {
t.Errorf("--no-shipped-data alone = %d, %q, %q, want a template reading no data rendered", code, out, errb)
}
if code, _, errb := runOut("{/sv_SE.person}", "--no-shipped-data"); code != 2 || !strings.Contains(errb, "no entry") {
t.Errorf("--no-shipped-data alone = %d, %q, want a reference to nothing refused", code, errb)
}
}
+1 -1
View File
@@ -104,7 +104,7 @@ func TestFakeTemplateErrors(t *testing.T) {
}{
{`{"x":"Q"}`, "missing string \"format\""},
{`"{x}"`, `no field "x"`},
{`"{digits(0)}"`, "must be positive"},
{`"{digits(-1)}"`, "must not be negative"},
{`name: {/no.such.path}`, "no entry"},
{`name: {..nope}`, "write {/nope}"},
} {
-6
View File
@@ -59,12 +59,6 @@ func TestNewLoadsAnyDirName(t *testing.T) {
}
}
func TestNewEmptyDirErrors(t *testing.T) {
if _, err := New(WithoutShippedData(), WithDataPath(writeData(t, nil))); err == nil {
t.Fatal("New(empty dir) = nil error")
}
}
// TestFoldersBecomeDotPaths checks the core of the new model: a subfolder is a
// namespace, so data/<loc>/person.json is reachable as "<loc>.person".
func TestFoldersBecomeDotPaths(t *testing.T) {
+17 -1
View File
@@ -166,6 +166,22 @@ func TestANameInAnInlineTemplate(t *testing.T) {
}
}
func TestAChoiceItemKeepsTheNamesItBinds(t *testing.T) {
f, n, both := engine(1), compiled(t, `{"format":"{x}","x":[{"format":"{v as w}{w}{w}","v":["a","b"]},"c"]}`), 0
for i := 0; i < 100; i++ {
switch renderOnce(f.drawState, n) {
case "aa", "bb":
both++
case "c":
default:
t.Fatal("a name bound in a choice's item read two picks")
}
}
if both == 0 {
t.Error("100 renders never drew the item binding a name")
}
}
func TestNameErrors(t *testing.T) {
cases := []struct {
name string
@@ -177,7 +193,7 @@ func TestNameErrors(t *testing.T) {
{"bound inside a repeat written first", map[string]string{"word": `["a","b"]`, "card": `{"format":"{a}{z}","a":{"format":"{/word as w}{w}","repeat":2},"z":"{/word as w}{w}"}`},
`name "w" is bound outside this repeat too`},
{"bound in a choice's item", map[string]string{"word": `["a","b"]`, "card": `{"format":"{x}{w}","x":["{/word as w}","b"]}`},
`a choice's item binds no name`},
`name "w" is bound at field "x", inside an item of a choice, which a read outside that item cannot see`},
{"bound outside the repeat too", map[string]string{"word": `["a","b"]`, "card": `{"format":"{/word as w}{x}","x":{"format":"{/word as w}{w}","repeat":2}}`},
`name "w" is bound outside this repeat too`},
{"a field too", map[string]string{"word": `["a","b"]`, "card": `{"format":"{/word as w}{w}","w":"x"}`},
-1
View File
@@ -53,7 +53,6 @@ func TestNodeCompileErrors(t *testing.T) {
`[{"format":"A","weight":0},{"format":"B","weight":0}]`, // weights sum to zero
`[{"format":"A","weight":1e308},{"format":"B","weight":1e308}]`, // weights overflow to +Inf
`{"format":"A","weight":"heavy"}`, // non-numeric weight
`{"format":"x","repeat":0}`, // repeat below 1
`{"format":"x","repeat":-2}`, // negative repeat
`{"format":"x","repeat":1.5}`, // non-integer repeat
`{"format":"x","repeat":"two"}`, // non-numeric repeat
+15 -5
View File
@@ -1,7 +1,6 @@
package fejkdata
import (
"errors"
"reflect"
"slices"
"strings"
@@ -54,10 +53,21 @@ func TestUserDataMayReferenceShipped(t *testing.T) {
}
}
func TestWithoutShippedDataNeedsASource(t *testing.T) {
_, err := New(WithoutShippedData())
if err == nil || !strings.Contains(err.Error(), "WithDataPath") || !errors.Is(err, ErrNoData) {
t.Fatalf("New(WithoutShippedData()) = %v, want ErrNoData naming WithDataPath", err)
func TestAGeneratorWithNoDataRendersWhatReadsNone(t *testing.T) {
for name, opts := range map[string][]Option{
"no source": {WithoutShippedData()},
"empty folder": {WithoutShippedData(), WithDataPath(t.TempDir())},
} {
f, err := New(opts...)
if err != nil {
t.Fatalf("%s: New = %v, want a generator holding no data", name, err)
}
if v, err := f.FakeTemplate("{digits(3)}"); err != nil || len(v) != 3 {
t.Errorf("%s: FakeTemplate = %q, %v, want three digits", name, v, err)
}
if _, err := f.Fake("x"); err == nil || !strings.Contains(err.Error(), "no entry") {
t.Errorf("%s: Fake(x) = %v, want a reference to nothing refused", name, err)
}
}
}
+3 -1
View File
@@ -51,7 +51,9 @@ func TestAPartWithNoEffectRendersAsWithoutIt(t *testing.T) {
`{"format":"{word as w}x","word":["a","b"]}`: `"x"`,
`null`: `""`,
`{"format":"{p}","p":{"format":"{x}","x":[null,"a"]}}`: `{"format":"{p}","p":{"format":"{x}","x":["","a"]}}`,
`"{int(5,5)}"`: `"5"`,
`"{int(5,5)}"`: `"5"`,
`{"format":"{x}","x":["a","b"],"repeat":0}`: `""`,
`"{digits(0)}{upper(0)}{lower(0)}{hex(0)}{base64(0)}{nanoid(0)}"`: `""`,
`"{float(1,1,2)}"`: `"1.00"`,
`"{date(1990-01-01,1990-01-01,'2006-01-02')}"`: `"1990-01-01"`,
`"{date(1990-01-01,1990-12-31,'x')}"`: `"x"`,