api/vendor/github.com/globalsign/mgo/bson/bson_corpus_spec_test_gener...

295 lines
6.3 KiB
Go

// +build ignore
package main
import (
"bytes"
"fmt"
"go/format"
"html/template"
"io/ioutil"
"log"
"path/filepath"
"strings"
"github.com/globalsign/mgo/internal/json"
)
func main() {
log.SetFlags(0)
log.SetPrefix(name + ": ")
var g Generator
fmt.Fprintf(&g, "// Code generated by \"%s.go\"; DO NOT EDIT\n\n", name)
src := g.generate()
err := ioutil.WriteFile(fmt.Sprintf("%s.go", strings.TrimSuffix(name, "_generator")), src, 0644)
if err != nil {
log.Fatalf("writing output: %s", err)
}
}
// Generator holds the state of the analysis. Primarily used to buffer
// the output for format.Source.
type Generator struct {
bytes.Buffer // Accumulated output.
}
// format returns the gofmt-ed contents of the Generator's buffer.
func (g *Generator) format() []byte {
src, err := format.Source(g.Bytes())
if err != nil {
// Should never happen, but can arise when developing this code.
// The user can compile the output to see the error.
log.Printf("warning: internal error: invalid Go generated: %s", err)
log.Printf("warning: compile the package to analyze the error")
return g.Bytes()
}
return src
}
// EVERYTHING ABOVE IS CONSTANT BETWEEN THE GENERATORS
const name = "bson_corpus_spec_test_generator"
func (g *Generator) generate() []byte {
testFiles, err := filepath.Glob("./specdata/specifications/source/bson-corpus/tests/*.json")
if err != nil {
log.Fatalf("error reading bson-corpus files: %s", err)
}
tests, err := g.loadTests(testFiles)
if err != nil {
log.Fatalf("error loading tests: %s", err)
}
tmpl, err := g.getTemplate()
if err != nil {
log.Fatalf("error loading template: %s", err)
}
tmpl.Execute(&g.Buffer, tests)
return g.format()
}
func (g *Generator) loadTests(filenames []string) ([]*testDef, error) {
var tests []*testDef
for _, filename := range filenames {
test, err := g.loadTest(filename)
if err != nil {
return nil, err
}
tests = append(tests, test)
}
return tests, nil
}
func (g *Generator) loadTest(filename string) (*testDef, error) {
content, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var testDef testDef
err = json.Unmarshal(content, &testDef)
if err != nil {
return nil, err
}
names := make(map[string]struct{})
for i := len(testDef.Valid) - 1; i >= 0; i-- {
if testDef.BsonType == "0x05" && testDef.Valid[i].Description == "subtype 0x02" {
testDef.Valid = append(testDef.Valid[:i], testDef.Valid[i+1:]...)
continue
}
name := cleanupFuncName(testDef.Description + "_" + testDef.Valid[i].Description)
nameIdx := name
j := 1
for {
if _, ok := names[nameIdx]; !ok {
break
}
nameIdx = fmt.Sprintf("%s_%d", name, j)
}
names[nameIdx] = struct{}{}
testDef.Valid[i].TestDef = &testDef
testDef.Valid[i].Name = nameIdx
testDef.Valid[i].StructTest = testDef.TestKey != "" &&
(testDef.BsonType != "0x05" || strings.Contains(testDef.Valid[i].Description, "0x00")) &&
!testDef.Deprecated
}
for i := len(testDef.DecodeErrors) - 1; i >= 0; i-- {
if strings.Contains(testDef.DecodeErrors[i].Description, "UTF-8") {
testDef.DecodeErrors = append(testDef.DecodeErrors[:i], testDef.DecodeErrors[i+1:]...)
continue
}
name := cleanupFuncName(testDef.Description + "_" + testDef.DecodeErrors[i].Description)
nameIdx := name
j := 1
for {
if _, ok := names[nameIdx]; !ok {
break
}
nameIdx = fmt.Sprintf("%s_%d", name, j)
}
names[nameIdx] = struct{}{}
testDef.DecodeErrors[i].Name = nameIdx
}
return &testDef, nil
}
func (g *Generator) getTemplate() (*template.Template, error) {
content := `package bson_test
import (
"encoding/hex"
"time"
. "gopkg.in/check.v1"
"github.com/globalsign/mgo/bson"
)
func testValid(c *C, in []byte, expected []byte, result interface{}) {
err := bson.Unmarshal(in, result)
c.Assert(err, IsNil)
out, err := bson.Marshal(result)
c.Assert(err, IsNil)
c.Assert(string(expected), Equals, string(out), Commentf("roundtrip failed for %T, expected '%x' but got '%x'", result, expected, out))
}
func testDecodeSkip(c *C, in []byte) {
err := bson.Unmarshal(in, &struct{}{})
c.Assert(err, IsNil)
}
func testDecodeError(c *C, in []byte, result interface{}) {
err := bson.Unmarshal(in, result)
c.Assert(err, Not(IsNil))
}
{{range .}}
{{range .Valid}}
func (s *S) Test{{.Name}}(c *C) {
b, err := hex.DecodeString("{{.Bson}}")
c.Assert(err, IsNil)
{{if .CanonicalBson}}
cb, err := hex.DecodeString("{{.CanonicalBson}}")
c.Assert(err, IsNil)
{{else}}
cb := b
{{end}}
var resultD bson.D
testValid(c, b, cb, &resultD)
{{if .StructTest}}var resultS struct {
Element {{.TestDef.GoType}} ` + "`bson:\"{{.TestDef.TestKey}}\"`" + `
}
testValid(c, b, cb, &resultS){{end}}
testDecodeSkip(c, b)
}
{{end}}
{{range .DecodeErrors}}
func (s *S) Test{{.Name}}(c *C) {
b, err := hex.DecodeString("{{.Bson}}")
c.Assert(err, IsNil)
var resultD bson.D
testDecodeError(c, b, &resultD)
}
{{end}}
{{end}}
`
tmpl, err := template.New("").Parse(content)
if err != nil {
return nil, err
}
return tmpl, nil
}
func cleanupFuncName(name string) string {
return strings.Map(func(r rune) rune {
if (r >= 48 && r <= 57) || (r >= 65 && r <= 90) || (r >= 97 && r <= 122) {
return r
}
return '_'
}, name)
}
type testDef struct {
Description string `json:"description"`
BsonType string `json:"bson_type"`
TestKey string `json:"test_key"`
Valid []*valid `json:"valid"`
DecodeErrors []*decodeError `json:"decodeErrors"`
Deprecated bool `json:"deprecated"`
}
func (t *testDef) GoType() string {
switch t.BsonType {
case "0x01":
return "float64"
case "0x02":
return "string"
case "0x03":
return "bson.D"
case "0x04":
return "[]interface{}"
case "0x05":
return "[]byte"
case "0x07":
return "bson.ObjectId"
case "0x08":
return "bool"
case "0x09":
return "time.Time"
case "0x0E":
return "string"
case "0x10":
return "int32"
case "0x12":
return "int64"
case "0x13":
return "bson.Decimal"
default:
return "interface{}"
}
}
type valid struct {
Description string `json:"description"`
Bson string `json:"bson"`
CanonicalBson string `json:"canonical_bson"`
Name string
StructTest bool
TestDef *testDef
}
type decodeError struct {
Description string `json:"description"`
Bson string `json:"bson"`
Name string
}