diff --git a/gentests/_testgen/testgen.go b/gentests/_testgen/testgen.go index 6bf0ab7..8ceb31f 100644 --- a/gentests/_testgen/testgen.go +++ b/gentests/_testgen/testgen.go @@ -32,27 +32,25 @@ func glob(pat string) string { func main() { var errorsEncountered bool cfg := new(xsdgen.Config) - cases := findXSDTestCases() + + xsdTestCases, err := findXSDTestCases() + if err != nil { + log.Fatal(err) + } cfg.Option(xsdgen.DefaultOptions...) - for _, dir := range cases { - data, err := ioutil.ReadFile(glob(filepath.Join(dir, "*.xsd"))) + for _, testCase := range xsdTestCases { + code, tests, err := genXSDTests(*cfg, testCase.doc, testCase.pkg) if err != nil { errorsEncountered = true - log.Print(err) - continue - } - tests, err := genXSDTests(*cfg, data, dir) - if err != nil { - errorsEncountered = true - log.Print(dir, ":", err) + log.Print(testCase.pkg) continue } else { - log.Printf("generated xsd tests for %s", dir) + log.Printf("generated xsd tests for %s", testCase.pkg) } - if err := writeTestFiles(tests, dir); err != nil { + if err := writeTestFiles(code, tests, testCase.pkg); err != nil { errorsEncountered = true - log.Print(dir, ":", err) + log.Print(testCase.pkg, ":", err) } } @@ -61,20 +59,22 @@ func main() { } } -func writeTestFiles(file *ast.File, pkg string) error { +func writeTestFiles(code, tests *ast.File, pkg string) error { testFilename := filepath.Join(pkg, pkg+"_test.go") - - src, err := gen.FormattedSource(file, testFilename) + testSrc, err := gen.FormattedSource(tests, testFilename) if err != nil { return err } - if err := ioutil.WriteFile(testFilename, src, 0666); err != nil { + if err := ioutil.WriteFile(testFilename, testSrc, 0666); err != nil { return err } - // This is needed so 'go build' works and automated CI doesn't complain. - buildFilename := filepath.Join(pkg, pkg+".go") - return ioutil.WriteFile(buildFilename, []byte("package "+pkg+"\n"), 0666) + codeFilename := filepath.Join(pkg, pkg+".go") + codeSrc, err := gen.FormattedSource(code, codeFilename) + if err != nil { + return err + } + return ioutil.WriteFile(codeFilename, codeSrc, 0666) } // Generates unit tests for xml marshal unmarshalling of @@ -85,25 +85,30 @@ func writeTestFiles(file *ast.File, pkg string) error { // the document described in the XML schema. // - Marshal the resulting file back into an XML document. // - Compare the two documents for equality. -func genXSDTests(cfg xsdgen.Config, data []byte, dir string) (*ast.File, error) { - cfg.Option(xsdgen.PackageName(dir)) - code, err := cfg.GenCode(data) +// +// Returns type definitions and unit tests as separate files. +func genXSDTests(cfg xsdgen.Config, data []byte, pkg string) (code, tests *ast.File, err error) { + cfg.Option(xsdgen.PackageName(pkg)) + main, err := cfg.GenCode(data) if err != nil { - return nil, err + return nil, nil, err } - file, err := code.GenAST() + code, err = main.GenAST() if err != nil { - return nil, err + return nil, nil, err } + tests = new(ast.File) + tests.Name = ast.NewIdent(pkg) + // We look for top-level elements in the schema to determine what // the example document looks like. roots, err := xsd.Normalize(data) if err != nil { - return nil, err + return nil, nil, err } if len(roots) < 1 { - return nil, fmt.Errorf("no schema in %s", dir) + return nil, nil, fmt.Errorf("no schema in %s", pkg) } root := roots[0] doc := topLevelElements(root) @@ -112,21 +117,21 @@ func genXSDTests(cfg xsdgen.Config, data []byte, dir string) (*ast.File, error) for _, elem := range doc { fields = append(fields, gen.Public(elem.Name.Local), - ast.NewIdent(code.NameOf(elem.Type)), + ast.NewIdent(main.NameOf(elem.Type)), gen.String(fmt.Sprintf(`xml:"%s %s"`, elem.Name.Space, elem.Name.Local))) } expr, err := gen.ToString(gen.Struct(fields...)) if err != nil { - return nil, err + return nil, nil, err } var params struct { DocStruct string - Dir string + Pkg string } params.DocStruct = expr - params.Dir = dir - fn, err := gen.Func("Test"+strings.Title(dir)). + params.Pkg = pkg + fn, err := gen.Func("Test"+strings.Title(pkg)). Args("t *testing.T"). BodyTmpl(` type Document {{.DocStruct}} @@ -155,7 +160,7 @@ func genXSDTests(cfg xsdgen.Config, data []byte, dir string) (*ast.File, error) inputTree, err := xmltree.Parse(input) if err != nil { - t.Fatal("{{.Dir}}: ", err) + t.Fatal("{{.Pkg}}: ", err) } outputTree, err := xmltree.Parse(output) @@ -171,11 +176,10 @@ func genXSDTests(cfg xsdgen.Config, data []byte, dir string) (*ast.File, error) `, params).Decl() if err != nil { - return nil, err + return nil, nil, err } - // Test goes at the top - file.Decls = append([]ast.Decl{fn}, file.Decls...) - return file, nil + tests.Decls = append(tests.Decls, fn) + return code, tests, nil } type Element struct { @@ -201,17 +205,29 @@ func topLevelElements(root *xmltree.Element) []Element { return result } +type testCase struct { + pkg string + doc []byte +} + // Looks for subdirectories containing pairs of (xml, xsd) files // that should contain an xml document and the schema it conforms to, // respectively. Returns slice of the directory names -func findXSDTestCases() []string { +func findXSDTestCases() ([]testCase, error) { filenames, err := filepath.Glob("*/*.xsd") if err != nil { - return nil + return nil, err } - result := make([]string, 0, len(filenames)) + result := make([]testCase, 0, len(filenames)) for _, xsdfile := range filenames { - result = append(result, filepath.Base(filepath.Dir(xsdfile))) + if data, err := ioutil.ReadFile(xsdfile); err != nil { + return nil, err + } else { + result = append(result, testCase{ + pkg: filepath.Base(filepath.Dir(xsdfile)), + doc: data, + }) + } } - return result + return result, nil } diff --git a/gentests/bindata/bindata.go b/gentests/bindata/bindata.go index e9ec81d..a610822 100644 --- a/gentests/bindata/bindata.go +++ b/gentests/bindata/bindata.go @@ -1 +1,66 @@ package bindata + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "encoding/xml" +) + +type Bindata struct { + HexData []byte `xml:"tns hexData"` + B64Data []byte `xml:"tns b64Data"` + Filename string `xml:"tns filename"` +} + +func (t *Bindata) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + type T Bindata + var layout struct { + *T + HexData *xsdHexBinary `xml:"tns hexData"` + B64Data *xsdBase64Binary `xml:"tns b64Data"` + } + layout.T = (*T)(t) + layout.HexData = (*xsdHexBinary)(&layout.T.HexData) + layout.B64Data = (*xsdBase64Binary)(&layout.T.B64Data) + return e.EncodeElement(layout, start) +} +func (t *Bindata) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + type T Bindata + var overlay struct { + *T + HexData *xsdHexBinary `xml:"tns hexData"` + B64Data *xsdBase64Binary `xml:"tns b64Data"` + } + overlay.T = (*T)(t) + overlay.HexData = (*xsdHexBinary)(&overlay.T.HexData) + overlay.B64Data = (*xsdBase64Binary)(&overlay.T.B64Data) + return d.DecodeElement(&overlay, &start) +} + +type xsdBase64Binary []byte + +func (b *xsdBase64Binary) UnmarshalText(text []byte) (err error) { + *b, err = base64.StdEncoding.DecodeString(string(text)) + return +} +func (b xsdBase64Binary) MarshalText() ([]byte, error) { + var buf bytes.Buffer + enc := base64.NewEncoder(base64.StdEncoding, &buf) + enc.Write([]byte(b)) + enc.Close() + return buf.Bytes(), nil +} + +type xsdHexBinary []byte + +func (b *xsdHexBinary) UnmarshalText(text []byte) (err error) { + *b, err = hex.DecodeString(string(text)) + return +} +func (b xsdHexBinary) MarshalText() ([]byte, error) { + n := hex.EncodedLen(len(b)) + buf := make([]byte, n) + hex.Encode(buf, []byte(b)) + return buf, nil +} diff --git a/gentests/bindata/bindata_test.go b/gentests/bindata/bindata_test.go index fa96802..9fab384 100644 --- a/gentests/bindata/bindata_test.go +++ b/gentests/bindata/bindata_test.go @@ -1,9 +1,6 @@ package bindata import ( - "bytes" - "encoding/base64" - "encoding/hex" "encoding/xml" "io/ioutil" "path/filepath" @@ -49,61 +46,3 @@ func TestBindata(t *testing.T) { t.Errorf("got \n%s\n, wanted \n%s\n", xmltree.MarshalIndent(outputTree, "", " "), xmltree.MarshalIndent(inputTree, "", " ")) } } - -type Bindata struct { - HexData []byte `xml:"tns hexData"` - B64Data []byte `xml:"tns b64Data"` - Filename string `xml:"tns filename"` -} - -func (t *Bindata) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - type T Bindata - var layout struct { - *T - HexData *xsdHexBinary `xml:"tns hexData"` - B64Data *xsdBase64Binary `xml:"tns b64Data"` - } - layout.T = (*T)(t) - layout.HexData = (*xsdHexBinary)(&layout.T.HexData) - layout.B64Data = (*xsdBase64Binary)(&layout.T.B64Data) - return e.EncodeElement(layout, start) -} -func (t *Bindata) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - type T Bindata - var overlay struct { - *T - HexData *xsdHexBinary `xml:"tns hexData"` - B64Data *xsdBase64Binary `xml:"tns b64Data"` - } - overlay.T = (*T)(t) - overlay.HexData = (*xsdHexBinary)(&overlay.T.HexData) - overlay.B64Data = (*xsdBase64Binary)(&overlay.T.B64Data) - return d.DecodeElement(&overlay, &start) -} - -type xsdBase64Binary []byte - -func (b *xsdBase64Binary) UnmarshalText(text []byte) (err error) { - *b, err = base64.StdEncoding.DecodeString(string(text)) - return -} -func (b xsdBase64Binary) MarshalText() ([]byte, error) { - var buf bytes.Buffer - enc := base64.NewEncoder(base64.StdEncoding, &buf) - enc.Write([]byte(b)) - enc.Close() - return buf.Bytes(), nil -} - -type xsdHexBinary []byte - -func (b *xsdHexBinary) UnmarshalText(text []byte) (err error) { - *b, err = hex.DecodeString(string(text)) - return -} -func (b xsdHexBinary) MarshalText() ([]byte, error) { - n := hex.EncodedLen(len(b)) - buf := make([]byte, n) - hex.Encode(buf, []byte(b)) - return buf, nil -} diff --git a/gentests/books/books.go b/gentests/books/books.go index 5ca8bcc..19a896d 100644 --- a/gentests/books/books.go +++ b/gentests/books/books.go @@ -1 +1,76 @@ package books + +import ( + "bytes" + "encoding/xml" + "time" +) + +type BookForm struct { + Author string `xml:"urn:books author"` + Title string `xml:"urn:books title"` + Genre string `xml:"urn:books genre"` + Price float32 `xml:"urn:books price"` + Pubdate time.Time `xml:"urn:books pub_date"` + Review string `xml:"urn:books review"` + Name string `xml:"name,attr,omitempty"` +} + +func (t *BookForm) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + type T BookForm + var layout struct { + *T + Pubdate *xsdDate `xml:"urn:books pub_date"` + } + layout.T = (*T)(t) + layout.Pubdate = (*xsdDate)(&layout.T.Pubdate) + return e.EncodeElement(layout, start) +} +func (t *BookForm) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + type T BookForm + var overlay struct { + *T + Pubdate *xsdDate `xml:"urn:books pub_date"` + } + overlay.T = (*T)(t) + overlay.Pubdate = (*xsdDate)(&overlay.T.Pubdate) + return d.DecodeElement(&overlay, &start) +} + +type BooksForm struct { + Book []BookForm `xml:"urn:books book,omitempty"` +} + +type xsdDate time.Time + +func (t *xsdDate) UnmarshalText(text []byte) error { + return _unmarshalTime(text, (*time.Time)(t), "2006-01-02") +} +func (t xsdDate) MarshalText() ([]byte, error) { + return []byte((time.Time)(t).Format("2006-01-02")), nil +} +func (t xsdDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + if (time.Time)(t).IsZero() { + return nil + } + m, err := t.MarshalText() + if err != nil { + return err + } + return e.EncodeElement(m, start) +} +func (t xsdDate) MarshalXMLAttr(name xml.Name) (xml.Attr, error) { + if (time.Time)(t).IsZero() { + return xml.Attr{}, nil + } + m, err := t.MarshalText() + return xml.Attr{Name: name, Value: string(m)}, err +} +func _unmarshalTime(text []byte, t *time.Time, format string) (err error) { + s := string(bytes.TrimSpace(text)) + *t, err = time.Parse(format, s) + if _, ok := err.(*time.ParseError); ok { + *t, err = time.Parse(format+"Z07:00", s) + } + return err +} diff --git a/gentests/books/books_test.go b/gentests/books/books_test.go index 92e5f9f..19ec68e 100644 --- a/gentests/books/books_test.go +++ b/gentests/books/books_test.go @@ -1,12 +1,10 @@ package books import ( - "bytes" "encoding/xml" "io/ioutil" "path/filepath" "testing" - "time" "aqwari.net/xml/xmltree" ) @@ -48,72 +46,3 @@ func TestBooks(t *testing.T) { t.Errorf("got \n%s\n, wanted \n%s\n", xmltree.MarshalIndent(outputTree, "", " "), xmltree.MarshalIndent(inputTree, "", " ")) } } - -type BookForm struct { - Author string `xml:"urn:books author"` - Title string `xml:"urn:books title"` - Genre string `xml:"urn:books genre"` - Price float32 `xml:"urn:books price"` - Pubdate time.Time `xml:"urn:books pub_date"` - Review string `xml:"urn:books review"` - Name string `xml:"name,attr,omitempty"` -} - -func (t *BookForm) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - type T BookForm - var layout struct { - *T - Pubdate *xsdDate `xml:"urn:books pub_date"` - } - layout.T = (*T)(t) - layout.Pubdate = (*xsdDate)(&layout.T.Pubdate) - return e.EncodeElement(layout, start) -} -func (t *BookForm) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - type T BookForm - var overlay struct { - *T - Pubdate *xsdDate `xml:"urn:books pub_date"` - } - overlay.T = (*T)(t) - overlay.Pubdate = (*xsdDate)(&overlay.T.Pubdate) - return d.DecodeElement(&overlay, &start) -} - -type BooksForm struct { - Book []BookForm `xml:"urn:books book,omitempty"` -} - -type xsdDate time.Time - -func (t *xsdDate) UnmarshalText(text []byte) error { - return _unmarshalTime(text, (*time.Time)(t), "2006-01-02") -} -func (t xsdDate) MarshalText() ([]byte, error) { - return []byte((time.Time)(t).Format("2006-01-02")), nil -} -func (t xsdDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - if (time.Time)(t).IsZero() { - return nil - } - m, err := t.MarshalText() - if err != nil { - return err - } - return e.EncodeElement(m, start) -} -func (t xsdDate) MarshalXMLAttr(name xml.Name) (xml.Attr, error) { - if (time.Time)(t).IsZero() { - return xml.Attr{}, nil - } - m, err := t.MarshalText() - return xml.Attr{Name: name, Value: string(m)}, err -} -func _unmarshalTime(text []byte, t *time.Time, format string) (err error) { - s := string(bytes.TrimSpace(text)) - *t, err = time.Parse(format, s) - if _, ok := err.(*time.ParseError); ok { - *t, err = time.Parse(format+"Z07:00", s) - } - return err -}