Skip to content
Snippets Groups Projects
Commit 87a159c4 authored by Neil Pankey's avatar Neil Pankey
Browse files

utf16: Schema errors and encoding tests

parent 806c9cd1
No related branches found
No related tags found
No related merge requests found
// +build ignore
// generates clones the utf-8 tests data to the other
// gen_testdata clones the utf-8 tests data to the other
// unicode encodings and adds BOM variants of each.
package main
......@@ -14,7 +14,6 @@ import (
"golang.org/x/text/encoding/unicode"
)
func main() {
var xforms = []struct {
dir, bom string
......
......@@ -79,7 +79,7 @@ func realMain(args []string, w io.Writer) int {
dir := filepath.Dir(list)
f, err := os.Open(list)
if err != nil {
log.Fatalf("%s: %s\n", list, err)
return schemaError("%s: %s", list, err)
}
defer f.Close()
......@@ -93,7 +93,7 @@ func realMain(args []string, w io.Writer) int {
docs = append(docs, glob(pattern)...)
}
if err := scanner.Err(); err != nil {
log.Fatalf("%s: invalid file list: %s\n", list, err)
return schemaError("%s: invalid file list: %s", list, err)
}
}
if len(docs) == 0 {
......@@ -104,13 +104,13 @@ func realMain(args []string, w io.Writer) int {
sl := gojsonschema.NewSchemaLoader()
schemaPath, err := filepath.Abs(*schemaFlag)
if err != nil {
log.Fatalf("%s: unable to convert to absolute path: %s\n", *schemaFlag, err)
return schemaError("%s: unable to convert to absolute path: %s", *schemaFlag, err)
}
for _, ref := range refFlags {
for _, p := range glob(ref) {
absPath, err := filepath.Abs(p)
if err != nil {
log.Fatalf("%s: unable to convert to absolute path: %s\n", absPath, err)
return schemaError("%s: unable to convert to absolute path: %s", absPath, err)
}
if absPath == schemaPath {
......@@ -119,22 +119,22 @@ func realMain(args []string, w io.Writer) int {
loader, err := jsonLoader(absPath)
if err != nil {
log.Fatalf("%s: unable to load schema ref: %s\n", *schemaFlag, err)
return schemaError("%s: unable to load schema ref: %s", *schemaFlag, err)
}
if err := sl.AddSchemas(loader); err != nil {
log.Fatalf("%s: invalid schema: %s\n", p, err)
return schemaError("%s: invalid schema: %s", p, err)
}
}
}
schemaLoader, err := jsonLoader(schemaPath)
if err != nil {
log.Fatalf("%s: unable to load schema: %s\n", *schemaFlag, err)
return schemaError("%s: unable to load schema: %s", *schemaFlag, err)
}
schema, err := sl.Compile(schemaLoader)
if err != nil {
log.Fatalf("%s: invalid schema: %s\n", *schemaFlag, err)
return schemaError("%s: invalid schema: %s", *schemaFlag, err)
}
// Validate the schema against each doc in parallel, limiting simultaneous
......@@ -262,8 +262,8 @@ func jsonDecodeCharset(buf []byte) ([]byte, error) {
func printUsage() {
fmt.Fprintf(os.Stderr, `Usage: %s -s schema.(json|yml) [options] document.(json|yml) ...
yajsv validates JSON and YAML document(s) against a schema. One of three statuses are
reported per document:
yajsv validates JSON and YAML document(s) against a schema. One of three status
results are reported per document:
pass: Document is valid relative to the schema
fail: Document is invalid relative to the schema
......@@ -273,7 +273,8 @@ func printUsage() {
schema validation failure.
Sets the exit code to 1 on any failures, 2 on any errors, 3 on both, 4 on
invalid usage. Otherwise, 0 is returned if everything passes validation.
invalid usage, 5 on schema definition or file-list errors. Otherwise, 0 is
returned if everything passes validation.
Options:
......@@ -288,6 +289,11 @@ func usageError(msg string) int {
return 4
}
func schemaError(format string, args ...interface{}) int {
fmt.Fprintf(os.Stderr, format+"\n", args...)
return 5
}
// glob is a wrapper that also resolves `~` since we may be skipping
// the shell expansion when single-quoting globs at the command line
func glob(pattern string) []string {
......
package main
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"testing"
)
func init() {
// TODO: Cleanup this global monkey-patching
devnull, err := os.Open(os.DevNull)
if err != nil {
panic(err)
}
os.Stderr = devnull
}
func TestMain(t *testing.T) {
tests := []struct {
in string
......@@ -14,6 +25,10 @@ func TestMain(t *testing.T) {
exit int
}{
{
"-s testdata/utf-16be_bom/schema.json testdata/utf-16le_bom/data-fail.yml",
[]string{},
5,
}, {
"-s testdata/utf-8/schema.yml testdata/utf-8/data-pass.yml",
[]string{"testdata/utf-8/data-pass.yml: pass"},
0,
......@@ -89,3 +104,80 @@ func TestMain(t *testing.T) {
})
}
}
func TestMatrix(t *testing.T) {
// schema.{format} {encoding}{_bom}/data-{expect}.{format}
type testcase struct {
schemaEnc, schemaFmt string
dataEnc, dataFmt, dataRes string
allowBOM bool
}
encodings := []string{"utf-8", "utf-16be", "utf-16le", "utf-8_bom", "utf-16be_bom", "utf-16le_bom"}
formats := []string{"json", "yml"}
results := []string{"pass", "fail", "error"}
tests := []testcase{}
// poor mans cartesian product
for _, senc := range encodings {
for _, sfmt := range formats {
for _, denc := range encodings {
for _, dfmt := range formats {
for _, dres := range results {
tests = append(tests, testcase{senc, sfmt, denc, dfmt, dres, false})
tests = append(tests, testcase{senc, sfmt, denc, dfmt, dres, true})
}
}
}
}
}
for _, tt := range tests {
schemaBOM := strings.HasSuffix(tt.schemaEnc, "_bom")
schema16 := strings.HasPrefix(tt.schemaEnc, "utf-16")
dataBOM := strings.HasSuffix(tt.dataEnc, "_bom")
data16 := strings.HasPrefix(tt.dataEnc, "utf-16")
schema := fmt.Sprintf("testdata/%s/schema.%s", tt.schemaEnc, tt.schemaFmt)
data := fmt.Sprintf("testdata/%s/data-%s.%s", tt.dataEnc, tt.dataRes, tt.dataFmt)
cmd := fmt.Sprintf("-s %s %s", schema, data)
if tt.allowBOM {
cmd = "-b " + cmd
}
t.Run(cmd, func(t *testing.T) {
want := 0
switch {
// Schema Errors (exit = 5)
// - YAML w/out BOM for UTF-16
// - JSON w/ BOM but missing allowBOM flag
case tt.schemaFmt == "yml" && !schemaBOM && schema16:
want = 5
case tt.schemaFmt == "json" && schemaBOM && !tt.allowBOM:
want = 5
// Data Errors (exit = 2)
// - YAML w/out BOM for UTF-16
// - JSON w/ BOM but missing allowBOM flag
// - standard malformed files (e.g. data-error)
case tt.dataFmt == "yml" && !dataBOM && data16:
want = 2
case tt.dataFmt == "json" && dataBOM && !tt.allowBOM:
want = 2
case tt.dataRes == "error":
want = 2
// Data Failures
case tt.dataRes == "fail":
want = 1
}
// TODO: Cleanup this global monkey-patching
*bomFlag = tt.allowBOM
var w strings.Builder
got := realMain(strings.Split(cmd, " "), &w)
if got != want {
t.Errorf("got(%d) != want(%d) bomflag %t", got, want, *bomFlag)
}
})
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment