Typed columns and null #13
@@ -69,6 +69,9 @@ func TestBuiltinFloat(t *testing.T) {
|
||||
if got := mustRender(t, f, `"{float(1,2,3)}"`); !re.MatchString(got) {
|
||||
t.Fatalf("float(1,2,3) = %q, want d.ddd in [1,2]", got)
|
||||
}
|
||||
if got := mustRender(t, f, `"{float(-1,1,0)}"`); got == "-0" {
|
||||
t.Fatalf("float(-1,1,0) = %q, want a zero printed unsigned", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ func TestCalcAuto(t *testing.T) {
|
||||
`"{calc(10 / 3)}"`: "3.3333333333333335",
|
||||
`"{calc(6 / 2)}"`: "3",
|
||||
`"{calc(1 / 4)}"`: "0.25",
|
||||
`"{calc(0 * -1)}"`: "0",
|
||||
}
|
||||
for tmpl, want := range cases {
|
||||
if got := mustRender(t, f, tmpl); got != want {
|
||||
@@ -52,6 +53,7 @@ func TestCalcDecimals(t *testing.T) {
|
||||
`"{calc(10 / 3, 2)}"`: "3.33",
|
||||
`"{calc(10 / 3, 0)}"`: "3",
|
||||
`"{calc(2 * 3, 2)}"`: "6.00",
|
||||
`"{calc(-0.001, 2)}"`: "0.00",
|
||||
}
|
||||
for tmpl, want := range cases {
|
||||
if got := mustRender(t, f, tmpl); got != want {
|
||||
|
||||
+16
-1
@@ -32,6 +32,7 @@ func TestDatatypeAndNullSitOnlyInAColumn(t *testing.T) {
|
||||
`null`: `so write ""`,
|
||||
`{"format":"{p}","p":{"format":"{x}","x":[null,"a"]}}`: `so write ""`,
|
||||
`{"format":"","c":[{"format":"1","datatype":"integer"},"x"]}`: `write it as {"format":"x","datatype":"integer"}`,
|
||||
`{"format":"","c":[{"format":"1","datatype":"integer"},{"format":"2","weight":3}]}`: `give it "datatype": "integer"`,
|
||||
`{"format":"","c":[{"format":"1","datatype":"integer"},{"format":"true","datatype":"boolean"}]}`: "a column holds one datatype",
|
||||
} {
|
||||
if _, err := compile(parse(t, src)); err == nil || !strings.Contains(err.Error(), want) {
|
||||
@@ -66,6 +67,17 @@ func TestDatatypeRejectsAValueItsTypeRejects(t *testing.T) {
|
||||
{"an overflow", `{"format":"{calc(a * a)}","a":"{digits(200)}","datatype":"number"}`, "is not proven within 1e300"},
|
||||
{"a division in an integer column", `{"format":"{calc(a / b)}","a":"{int(1,9)}","b":"{int(1,9)}","datatype":"integer"}`, "prints a number, not an integer"},
|
||||
{"a composed operand", `{"format":"{calc(n * 2)}","n":"{int(1,99)}.{digits(2)}","datatype":"number"}`, "{seq()}, {digits()} or {calc()}"},
|
||||
{"a divisor that prints as zero", `{"format":"{calc(1 / b, 2)}","b":"{float(4.9999999999999994e-79,5e-79,78)}","datatype":"number"}`, "divides by b, which is not proven nonzero"},
|
||||
{"a divisor that rounds to zero", `{"format":"{calc(a / b)}","a":"{int(1,9)}","b":"{float(0.1,1,0)}","datatype":"number"}`, "divides by b, which is not proven nonzero"},
|
||||
{"a zero among a divisor's literals", `{"format":"{calc(a / b)}","a":"{int(1,9)}","b":["0","5"],"datatype":"number"}`, "divides by b, which is not proven nonzero"},
|
||||
{"a negated divisor crossing zero", `{"format":"{calc(a / (-b + 10))}","a":"{int(1,9)}","b":"{int(1,20)}","datatype":"number"}`, "which is not proven nonzero"},
|
||||
{"a subtracted divisor crossing zero", `{"format":"{calc(a / (10 - b))}","a":"{int(1,9)}","b":"{int(1,20)}","datatype":"number"}`, "which is not proven nonzero"},
|
||||
{"a quotient past the limit", `{"format":"{calc(a / b / b)}","a":"{digits(300)}","b":"{float(0.000001,1,6)}","datatype":"number"}`, "is not proven within 1e300"},
|
||||
{"an operand past the limit", `{"format":"{calc(a)}","a":"{digits(400)}","datatype":"number"}`, "is not proven within 1e300"},
|
||||
{"a whole calc past int64", `{"format":"{calc(a * 2)}","a":"{seq()}","datatype":"integer"}`, "{calc(a * 2)} is not proven within int64"},
|
||||
{"a whole float past int64", `{"format":"{float(0,1e19,0)}","datatype":"integer"}`, "{float(0,1e19,0)} is not proven within int64"},
|
||||
{"a signed zero integer", `{"format":"-0","datatype":"integer"}`, `"-0" is zero written with a sign; write "0"`},
|
||||
{"a signed zero number", `{"format":"-0.00","datatype":"number"}`, `"-0.00" is zero written with a sign; write "0.00"`},
|
||||
} {
|
||||
row := `{"format":"","col":` + c.column + `}`
|
||||
files := map[string]string{"row": row}
|
||||
@@ -83,7 +95,7 @@ func TestDatatypeRejectsAValueItsTypeRejects(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var jsonInteger = regexp.MustCompile(`^-?(0|[1-9][0-9]*)$`)
|
||||
var jsonInteger = regexp.MustCompile(`^(0|-?[1-9][0-9]*)$`)
|
||||
|
||||
func TestDatatypeAcceptsAColumnThatAlwaysParses(t *testing.T) {
|
||||
cat := `[{"format":"{code}","code":"200"},{"format":"{code}","code":"404"}]`
|
||||
@@ -94,6 +106,9 @@ func TestDatatypeAcceptsAColumnThatAlwaysParses(t *testing.T) {
|
||||
`{"format":"{/cat.code}","datatype":"integer"}`,
|
||||
`{"format":"{a|b}","a":"1","b":"{int(5,9)}","datatype":"integer"}`,
|
||||
`{"format":"{float(-1.5,9.5,0)}","datatype":"integer"}`,
|
||||
`{"format":"{float(-1,1,0)}","datatype":"integer"}`,
|
||||
`{"format":"{calc(a * b)}","a":"{int(-9,-1)}","b":"{int(0,9)}","datatype":"integer"}`,
|
||||
`{"format":"{calc(x + 1)}","x":"{float(0,9,0)}","datatype":"integer"}`,
|
||||
`{"format":"{float(-1,1,2)}","datatype":"number"}`,
|
||||
`{"format":"{v}","v":["1","2.5","6.022e23"],"datatype":"number"}`,
|
||||
`{"format":"{b}","b":["true","false"],"datatype":"boolean"}`,
|
||||
|
||||
@@ -18,6 +18,7 @@ func TestRepeatedChoiceItemIsRejected(t *testing.T) {
|
||||
`["a", "a", "b"]`: `{ "format": "a", "weight": 2 }`,
|
||||
`[{"format":"{x}","x":"1"},{"format":"{x}","x":"1"}]`: "repeats item",
|
||||
`{"format":"{w}","w":["", "", "x"]}`: `{ "format": "", "weight": 2 }`,
|
||||
`{"format":"","w":[null,null,"a"]}`: "a null takes no weight",
|
||||
} {
|
||||
if _, err := compile(parse(t, src)); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("compile(%s) = %v, want an error naming %s", src, err, want)
|
||||
|
||||
@@ -68,6 +68,10 @@ func TestNoRecordAllocRegression(t *testing.T) {
|
||||
if allocs := testing.AllocsPerRun(10000, func() { f.FakeRecord("x") }); allocs > base*1.10 {
|
||||
t.Errorf("%s: %.1f allocs/op regressed past %.1f (baseline %.1f + 10%%); a record fence running per draw is the usual cause", s.name, allocs, base*1.10, base)
|
||||
}
|
||||
r, _ := f.FakeRecord("x")
|
||||
if allocs := testing.AllocsPerRun(10000, func() { _ = r.CSVLine() }); allocs > 2 {
|
||||
t.Errorf("%s: CSVLine() makes %.1f allocs/op, want 2: the fields slice and the joined line", s.name, allocs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-4
@@ -134,7 +134,7 @@ func TestRecordCSV(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRecordCSVEmptyValueStaysARow(t *testing.T) {
|
||||
dir := writeData(t, map[string]string{"blank": `{"format": "", "note": ""}`, "gone": `{"format": "", "note": null}`})
|
||||
dir := writeData(t, map[string]string{"blank": `{"format": "", "note": ""}`})
|
||||
f := newGenerator(t, dir, WithSeed(1))
|
||||
r, err := f.FakeRecord("blank")
|
||||
if err != nil {
|
||||
@@ -147,9 +147,6 @@ func TestRecordCSVEmptyValueStaysARow(t *testing.T) {
|
||||
if len(rows) != 2 || len(rows[1]) != 1 || rows[1][0] != "" {
|
||||
t.Fatalf("one empty column parsed to %v, want a header and one row of one empty field", rows)
|
||||
}
|
||||
if r, err := f.FakeRecord("gone"); err != nil || r.CSVLine() != "" {
|
||||
t.Fatalf("a lone null column wrote %q, %v; want the blank line PostgreSQL's COPY reads as null", r.CSVLine(), err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordWritesTypedAndNullColumns(t *testing.T) {
|
||||
@@ -183,6 +180,13 @@ func TestRecordWritesTypedAndNullColumns(t *testing.T) {
|
||||
if got := DataTypeNumber.String(); got != "number" {
|
||||
t.Errorf("DataTypeNumber.String() = %q, want the data's spelling", got)
|
||||
}
|
||||
lone, err := newGenerator(t, writeData(t, map[string]string{"gone": `{"format":"","note":null}`})).FakeRecord("gone")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := lone.CSVLine(); got != "" {
|
||||
t.Errorf("a lone null column wrote CSVLine() = %q, want the blank line PostgreSQL's COPY reads as null", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordRejectsOverlappingReferenceColumns(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user