]> Cypherpunks repositories - keks.git/commitdiff
Schema integration tests
authorSergey Matveev <stargrave@stargrave.org>
Wed, 18 Jun 2025 12:35:27 +0000 (15:35 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Wed, 18 Jun 2025 12:52:50 +0000 (15:52 +0300)
c/lib/schema.c
go/cmd/schema-validate/main.go
go/schema/check.go
spec/schema/tcl
tcl/schema.t/generic.t [new file with mode: 0755]
tcl/schema.t/specs-our.t [new file with mode: 0755]
tcl/schema.t/specs-wheres.t [new file with mode: 0755]
tcl/schema.tcl

index 2301f27be2bbe296aaa987a83051f80ec769e6c73ec270d5af68692b031822f4..c253b2aaf7deeca82e612ddd9394ea11ef5b98bee3119531fa656779dc616fa9 100644 (file)
@@ -490,9 +490,8 @@ Eached:
             return err;
         }
         err.msg = "TIMEPREC";
-        if (v == SIZE_MAX) {
-            err.code = KEKSSchemaErrNo;
-        } else {
+        err.code = KEKSSchemaErrNo;
+        if (v != SIZE_MAX) {
             if (data->list[v].atom.typ != KEKSItemTAI64) {
                 err.code = KEKSSchemaErrUnexpectedState;
                 err.msg = "non-TAI64 taken";
@@ -504,18 +503,21 @@ Eached:
                 if (data->list[v].atom.v.str.len > 8) {
                     err.code = KEKSSchemaErrInvalidData;
                     err.msg = ">TAI64";
+                    return err;
                 }
                 break;
             case 3: // ms
                 if (data->list[v].atom.v.str.len > 12) {
                     err.code = KEKSSchemaErrInvalidData;
                     err.msg = ">TAI64N";
+                    return err;
                 }
                 if (data->list[v].atom.v.str.len > 8) {
                     val = (uint32_t)keksFromBE(data->list[v].atom.v.str.ptr + 8, 4);
                     if ((val % 1000000) != 0) {
                         err.code = KEKSSchemaErrInvalidData;
                         err.msg = ">ms";
+                        return err;
                     }
                 }
                 break;
@@ -523,12 +525,14 @@ Eached:
                 if (data->list[v].atom.v.str.len > 12) {
                     err.code = KEKSSchemaErrInvalidData;
                     err.msg = ">TAI64N";
+                    return err;
                 }
                 if (data->list[v].atom.v.str.len > 8) {
                     val = (uint32_t)keksFromBE(data->list[v].atom.v.str.ptr + 8, 4);
                     if ((val % 1000) != 0) {
                         err.code = KEKSSchemaErrInvalidData;
                         err.msg = ">µs";
+                        return err;
                     }
                 }
                 break;
@@ -536,6 +540,7 @@ Eached:
                 if (data->list[v].atom.v.str.len > 12) {
                     err.code = KEKSSchemaErrInvalidData;
                     err.msg = ">TAI64N";
+                    return err;
                 }
                 break;
             case 12: // ps
@@ -544,6 +549,7 @@ Eached:
                     if ((val % 1000000) != 0) {
                         err.code = KEKSSchemaErrInvalidData;
                         err.msg = ">ps";
+                        return err;
                     }
                 }
                 break;
@@ -553,17 +559,15 @@ Eached:
                     if ((val % 1000) != 0) {
                         err.code = KEKSSchemaErrInvalidData;
                         err.msg = ">fs";
+                        return err;
                     }
                 }
                 break;
-            case 18: // as
-                break;
             default:
                 err.code = KEKSSchemaErrInvalidSchema;
                 err.msg = "unknown TIMEPREC value";
                 return err;
             }
-            err.code = KEKSSchemaErrNo;
         }
     } else if (KEKSStrEqual(&(schema->list[idxSchema].atom), CmdUTC)) {
         err.msg = "UTC";
index f78673b56fc8ab90117f363ae7bd616d6448bfd76dd4a7a994e490a60d101b57..c3cbb4c13a9ca07fc90277a28719b384e7cef5b658cb1894215a566ffb97ffcf 100644 (file)
 package main
 
 import (
+       "bufio"
        "errors"
        "flag"
        "fmt"
+       "io"
        "log"
        "os"
 
@@ -29,15 +31,15 @@ import (
 func main() {
        flag.Parse()
        log.SetFlags(0)
-       if flag.NArg() != 3 {
-               fmt.Fprintf(os.Stderr, "Usage: schema-validate SCHEMA.keks SCHEMA-NAME DATA.keks\n")
+       if flag.NArg() != 2 {
+               fmt.Fprintf(os.Stderr, "Usage: schema-validate SCHEMA.keks SCHEMA-NAME <DATA.keks\n")
                os.Exit(1)
        }
        schemasRaw, err := os.ReadFile(flag.Arg(0))
        if err != nil {
                log.Fatal(err)
        }
-       dataRaw, err := os.ReadFile(flag.Arg(2))
+       dataRaw, err := io.ReadAll(bufio.NewReader(os.Stdin))
        if err != nil {
                log.Fatal(err)
        }
@@ -55,7 +57,7 @@ func main() {
                log.Fatal(err)
        }
 
-       d = keks.NewDecoderFromBytes(dataRaw, nil)
+       d = keks.NewDecoderFromBytes(dataRaw, &keks.DecodeOpts{LeaveTAI64: true})
        data, err := d.Decode()
        if err != nil {
                log.Fatal(err)
index 9bb2f62af5da949340d11ffe7555086134ce17d43fc25c146bcb796485b1f2f4..0d74f8ce433762b8d48ef2892acd022252f3a06de704f3a9b33123b0d9c6f6ea 100644 (file)
@@ -609,8 +609,6 @@ func Check(schemaName string, schemas map[string][][]any, data any) error {
                                                        return &err
                                                }
                                        }
-                               case 18:
-                                       err.Msg = "as"
                                default:
                                        return &SchemaErr{BaseErr: BaseErr{
                                                SchemaName: schemaName,
index 857c0db0ac7fec2c45973b7f203d40002d7e2a846a92a7b1534f08c6281bf676..8ffd34819c42c8dc1e86e3f97cd47ec2e347441a070e7f60bf986bae0dbaa48f 100644 (file)
@@ -14,7 +14,18 @@ Example with "our" structure (from [schema/cmds]) can be written as:
         {field comment {str} optional}
     }
 
-and [cm/pub/] as:
+"wheres" as:
+
+    latitude {{field . {int} >-91 <91}}
+    longitude {{field . {int} >-181 <181}}
+    where {
+        {field . {list} len=2}
+        {field 0 {with latitude}}
+        {field 1 {with longitude}}
+    }
+    wheres {{field . {list} {of where} >0}}
+
+And [cm/pub/] as:
 
 <<    [schemas/pub.tcl]\r
 <<    [schemas/fpr.tcl]\r
@@ -51,7 +62,7 @@ empty.
 (use "len=" for integers).
 
 "prec=p" issues TIMEPREC command, but instead of specifying the raw
-integer values, you choose one of: s, ms, us, ns, ps, fs, as.
+integer values, you choose one of: s, ms, us, ns, ps, fs.
 
 "utc" issues UTC command.
 
diff --git a/tcl/schema.t/generic.t b/tcl/schema.t/generic.t
new file mode 100755 (executable)
index 0000000..dde238e
--- /dev/null
@@ -0,0 +1,574 @@
+#!/bin/sh
+
+test_description="Various generic tests"
+. $SHARNESS_TEST_SRCDIR/sharness.sh
+root=$(realpath ../..)
+SCHEMA_VALIDATE=${SCHEMA_VALIDATE:-schema-validate}
+export TCLLIBPATH=$root
+
+cat >schema.tcl <<EOF
+e {{field . {str bin}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+STR ok
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "type ok" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+BIN ok
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "type ok" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+INT 123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "type !ok" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field . {set}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo NIL bar NIL baz NIL}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "set ok" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo NIL bar NIL baz {INT 123}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "set !ok" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+SET {foo bar baz}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "set macro" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field . {map}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo {INT 123} bar {STR hello}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map various" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {map} {of int}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo {INT 123} bar {STR hello}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map !of int" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo {INT 123} bar {INT 234}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map of int" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+v {{field . {int}}}
+e {{field . {map} {of v}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo {INT 123} bar {INT 234}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map of int schema" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field . {list}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {{INT 123} {STR hello}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "list various" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {list} {of int}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {{INT 123} {STR hello}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "list !of int" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {{INT 123} {INT 234}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "list of int" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+v {{field . {int}}}
+e {{field . {list} {of v}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {{INT 123} {INT 234}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "list of int schema" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+v {{field . {int}}}
+e {{field . {with v}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+STR foo
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "!only schema" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+INT -123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "only schema" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field bar {int}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo NIL bar {INT 0}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map exists" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo NIL baz {INT 0}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map !exists" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field bar {int} optional}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo NIL bar {INT 0}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map optional" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo NIL baz {INT 0}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map optional !exists" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo NIL bar NIL}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map optional bad type" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field bar {} !exists}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo NIL bar {INT 0}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map !!exists" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {foo NIL baz {INT 0}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map !exists" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field . {int} >0}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+INT 0
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "int !>0" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+INT 1
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "int >0" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {int} >-1000}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+INT -1000
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "int !>-999" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+INT -999
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "int >-999" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field . {str} >0}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+STR ""
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "str !>0" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+STR "a"
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "str >0" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field . {list} >0}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "list !>0" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {NIL}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "list >0" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field . {map} >0}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map !>0" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+SET {a}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map >0" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field . {map} len=0}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map len=0" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {list} len=0}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "list len=0" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {str} len=0}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+STR ""
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "str len=0" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {int} len=0}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+INT 0
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "int len=0" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field . {map} len=3}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+SET {a b c}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "map len=3" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {list} len=3}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {NIL NIL NIL}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "list len=3" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {str} len=3}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+STR foo
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "str len=3" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {int} len=3}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+INT 3
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "int len=0" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+fpr {{field . {bin} len=4}}
+e {
+    {field . {map}}
+    {field fpr {with fpr} optional}
+}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "fpr !exists" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {fpr {STR 1234}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "fpr bad type" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {fpr {BIN 1234}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "fpr ok" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field . {tai}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123 123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 ok" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 [expr 0x586846A4]
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 !utc" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {tai} utc}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 [expr 0x586846A4]
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 !!utc" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+cat >schema.tcl <<EOF
+e {{field . {tai} prec=s}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 prec=s" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 !prec=s" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {tai} prec=ms}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123000000
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 prec=ms" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123000
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 !prec=ms" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {tai} prec=us}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123000
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 prec=us" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 !prec=us" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {tai} prec=ns}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 prec=ns" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 prec=ns" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123 123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 !prec=ns" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {tai} prec=ps}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123 123000000
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 prec=ps" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 prec=ps" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123 123000
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 !prec=ps" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {tai} prec=fs}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123 123000
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 prec=fs" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123 123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 !prec=fs" "! $SCHEMA_VALIDATE schema.keks e <data.keks"
+
+cat >schema.tcl <<EOF
+e {{field . {tai}}}
+EOF
+$root/schema.tcl schema.tcl | xxd -r -p >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+TAI64 123 123 123
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "tai64 prec=fs" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+########################################################################
+
+$root/keks.tcl >schema.keks.hex <<EOF
+MAGIC schema
+MAP {e {LIST {
+    {LIST {{STR .} {STR .}}}
+    {LIST {{STR T} {STR LIST}}}
+    {LIST {{STR *}}}
+    {LIST {{STR T} {STR INT}}}
+    {LIST {{STR >} {INT 100}}}
+    {LIST {{STR <} {INT 300}}}
+}}}
+EOF
+xxd -r -p <schema.keks.hex >schema.keks
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {{INT 123} {INT 234}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "manual int ranges" "$SCHEMA_VALIDATE schema.keks e <data.keks"
+
+test_done
diff --git a/tcl/schema.t/specs-our.t b/tcl/schema.t/specs-our.t
new file mode 100755 (executable)
index 0000000..70aaf70
--- /dev/null
@@ -0,0 +1,121 @@
+#!/bin/sh
+
+test_description="Check \"our\" specs/ example"
+. $SHARNESS_TEST_SRCDIR/sharness.sh
+root=$(realpath ../..)
+SCHEMA_VALIDATE=${SCHEMA_VALIDATE:-schema-validate}
+export TCLLIBPATH=$root
+
+cat >our.schema.tcl <<EOF
+ai {{field . {str} >0}}
+fpr {{field . {bin} len=32}}
+our {
+    {field a {with ai}}
+    {field v {bin str}}
+    {field fpr {with fpr}}
+    {field comment {str} optional}
+}
+EOF
+$root/schema.tcl our.schema.tcl | xxd -r -p >our.schema.keks
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {
+    a {STR some-ai}
+    v {BIN [binary decode hex "DEADBEEF"]}
+    fpr {BIN [binary decode hex [string repeat "AB" 32]]}
+    comment {STR привет}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "ok" "$SCHEMA_VALIDATE our.schema.keks our <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {
+    a {STR some-ai}
+    v {BIN [binary decode hex "DEADBEEF"]}
+    fpr {BIN [binary decode hex [string repeat "AB" 32]]}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "no comment" "$SCHEMA_VALIDATE our.schema.keks our <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {
+    a {STR some-ai}
+    v {BIN [binary decode hex "DEADBEEF"]}
+    fpr {BIN [binary decode hex [string repeat "AB" 32]]}
+    comment {STR ""}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "empty comment" "$SCHEMA_VALIDATE our.schema.keks our <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {
+    a {STR some-ai}
+    v {BIN [binary decode hex "DEADBEEF"]}
+    fpr {BIN [binary decode hex [string repeat "AB" 33]]}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "fpr longer" "! $SCHEMA_VALIDATE our.schema.keks our <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {
+    a {STR some-ai}
+    v {BIN [binary decode hex "DEADBEEF"]}
+    fpr {BIN [binary decode hex [string repeat "AB" 31]]}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "fpr shorter" "! $SCHEMA_VALIDATE our.schema.keks our <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {
+    a {STR some-ai}
+    v {BIN ""}
+    fpr {BIN [binary decode hex [string repeat "AB" 32]]}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "v empty" "$SCHEMA_VALIDATE our.schema.keks our <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {
+    a {STR ""}
+    v {STR whatever}
+    fpr {BIN [binary decode hex [string repeat "AB" 32]]}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "a empty" "! $SCHEMA_VALIDATE our.schema.keks our <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {
+    a {INT 123}
+    v {BIN [binary decode hex "DEADBEEF"]}
+    fpr {BIN [binary decode hex [string repeat "AB" 32]]}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "a bad type" "! $SCHEMA_VALIDATE our.schema.keks our <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {
+    a {STR some-ai}
+    v {BIN [binary decode hex "DEADBEEF"]}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "fpr no" "! $SCHEMA_VALIDATE our.schema.keks our <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+MAP {
+    v {BIN [binary decode hex "DEADBEEF"]}
+    fpr {BIN [binary decode hex [string repeat "AB" 32]]}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "a no" "! $SCHEMA_VALIDATE our.schema.keks our <data.keks"
+
+test_done
diff --git a/tcl/schema.t/specs-wheres.t b/tcl/schema.t/specs-wheres.t
new file mode 100755 (executable)
index 0000000..ef2f584
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description="Check \"wheres\" specs/ example"
+. $SHARNESS_TEST_SRCDIR/sharness.sh
+root=$(realpath ../..)
+SCHEMA_VALIDATE=${SCHEMA_VALIDATE:-schema-validate}
+export TCLLIBPATH=$root
+
+cat >wheres.schema.tcl <<EOF
+latitude {{field . {int} >-91 <91}}
+longitude {{field . {int} >-181 <181}}
+where {
+    {field . {list} len=2}
+    {field 0 {with latitude}}
+    {field 1 {with longitude}}
+}
+wheres {{field . {list} {of where} >0}}
+EOF
+$root/schema.tcl wheres.schema.tcl | xxd -r -p >wheres.schema.keks
+
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "wheres empty" "! $SCHEMA_VALIDATE wheres.schema.keks wheres <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {{LIST {}}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "where empty" "! $SCHEMA_VALIDATE wheres.schema.keks wheres <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {{LIST {{INT 45} {INT 180}}}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "ok" "$SCHEMA_VALIDATE wheres.schema.keks wheres <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {{LIST {{INT -45} {INT 180}}}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "lat negative" "$SCHEMA_VALIDATE wheres.schema.keks wheres <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {{LIST {{INT 45} {INT -180}}}}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "lon negative" "$SCHEMA_VALIDATE wheres.schema.keks wheres <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {
+    {LIST {{INT 45} {INT 180}}}
+    {LIST {{INT -91} {INT 180}}}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "oob lat" "! $SCHEMA_VALIDATE wheres.schema.keks wheres <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {
+    {LIST {{INT 45} {INT 180}}}
+    {LIST {{INT 90} {INT -181}}}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "oob lon" "! $SCHEMA_VALIDATE wheres.schema.keks wheres <data.keks"
+
+$root/keks.tcl >data.keks.hex <<EOF
+LIST {
+    {LIST {{INT 45} {INT 180}}}
+    {LIST {{STR 0} {INT 123}}}
+    {LIST {{INT 12}}}
+}
+EOF
+xxd -r -p <data.keks.hex >data.keks
+test_expect_success "lon bad len" "! $SCHEMA_VALIDATE wheres.schema.keks wheres <data.keks"
+
+test_done
index 6e958d98a6cf0db25886ef26d73860f30ac57f40fdcb19b013ef93f827e68333..e72711844bc51ef6c1fc2dea85a8eb996f7d12d84108b2730228ceb977451d08 100755 (executable)
@@ -45,7 +45,7 @@ proc SCHEMA {s} {subst {LIST {{STR S} {STR $s}}}}
 proc TIMEPREC {p} {subst {LIST {{STR TP} {INT $p}}}}
 proc UTC {} {return {LIST {{STR UTC}}}}
 
-set timeprecArgs [dict create s 0 ms 3 us 6 ns 9 ps 12 fs 15 as 18]
+set timeprecArgs [dict create s 0 ms 3 us 6 ns 9 ps 12 fs 15]
 set knownTypes {bin blob bool hexlet int list magic map nil set str tai}
 
 proc field {k types args} {