1
0
Fork 0
mirror of https://github.com/imjasonh/csvstruct synced 2026-07-07 02:32:20 +00:00
csvstruct/encode_test.go

279 lines
5.7 KiB
Go
Raw Permalink Normal View History

package csvstruct
import (
"bytes"
"net"
"strings"
"testing"
)
// Accounts for changes between Go 1.3 and 1.4 that changed how encoding/csv encodes empty strings
// See https://github.com/golang/go/commit/6ad2749dcd614270ac58c5254b6ada3bce0af090
// Set this to true if tests are failing against Go 1.3 or before
const backcompat = false
func TestEncodeNext(t *testing.T) {
type row struct {
Foo, Bar, Baz string
}
for _, c := range []struct {
rows []interface{}
2014-12-21 00:15:59 -05:00
want string
}{{
[]interface{}{row{"a", "b", "c"}, row{"d", "e", "f"}},
`Foo,Bar,Baz
a,b,c
2014-05-15 14:03:35 -04:00
d,e,f
`,
}, {
[]interface{}{row{"a", "", ""}, row{"", "b", ""}},
`Foo,Bar,Baz
a,,
,b,
`,
}, {
2014-05-16 11:55:14 -04:00
// Encoding incomplete structs still fills in missing columns.
[]interface{}{row{Foo: "a"}, struct{ Foo, Bar string }{Bar: "b"}},
`Foo,Bar,Baz
a,,
,b,
`,
}, {
// Encoding unexported fields.
[]interface{}{struct{ Exported, unexported string }{"a", "b"}},
`Exported
a
`,
}, {
// Encoding renamed and ignored fields.
[]interface{}{struct {
Foo string `csv:"renamed_foo"`
Bar string
Ignored string `csv:"-"`
Baz string
}{"a", "b", "c", "d"}},
`renamed_foo,Bar,Baz
a,b,d
`,
2014-05-16 11:55:14 -04:00
}, {
// If the first row contains no encodable fields, further rows
// will be ignored as well, resulting in an empty output.
[]interface{}{struct {
Ignored string `csv:"-"`
unexported string
}{"you", "won't"}, struct {
Exported string
}{"see"}},
"",
}, {
// Encoding non-string fields.
[]interface{}{struct {
Int int
Int64 int64
Uint64 uint64
Float64 float64
Bool bool
}{123, -123456789, 123456789, 123.456, true}},
`Int,Int64,Uint64,Float64,Bool
123,-123456789,123456789,123.456000,true
`,
}, {
// Encoding rows with different fields.
// Headers are taken from the fields in the first call to EncodeNext.
// Further calls add whatever fields they can, and if no fields are
// shared then the row is not written.
[]interface{}{
struct{ Foo, Bar string }{"foo", "bar"},
struct{ Baz string }{"baz"}, // Will be skipped because it shares no fields.
2014-06-18 12:10:56 -04:00
struct{ Bar, Baz string }{"bar", "baz"}}, // Only shares Bar, only writes Bar.
`Foo,Bar
foo,bar
,bar
2014-06-18 12:10:56 -04:00
`,
}, {
// Encoding rows with the same fields but with different types.
[]interface{}{
struct{ Foo string }{"foo"},
struct{ Foo int64 }{123},
struct{ Foo bool }{true}},
`Foo
foo
123
true
2014-05-15 14:03:35 -04:00
`,
}} {
var buf bytes.Buffer
e := NewEncoder(&buf)
for _, r := range c.rows {
if err := e.EncodeNext(r); err != nil {
t.Errorf("EncodeNext(%v): %v", r, err)
}
}
got := buf.String()
if backcompat {
got = strings.Replace(got, `""`, "", -1)
}
if got != c.want {
t.Errorf("EncodeNext(%v): got %s, want %s", c.rows, got, c.want)
}
}
}
func TestEncode_Opts(t *testing.T) {
rows := []struct{ A, B, C string }{
{"a", "b", "c"},
{"d", "e", "f"}}
for _, c := range []struct {
opts EncodeOpts
2014-12-21 00:15:59 -05:00
want string
}{{
EncodeOpts{Comma: '%'},
`A%B%C
a%b%c
d%e%f
`,
}, {
2014-07-17 18:22:24 -04:00
EncodeOpts{SkipHeader: true},
`a,b,c
d,e,f
`,
}, {
EncodeOpts{UseCRLF: true},
"A,B,C\r\na,b,c\r\nd,e,f\r\n",
}} {
var buf bytes.Buffer
e := NewEncoder(&buf).Opts(c.opts)
for _, r := range rows {
if err := e.EncodeNext(r); err != nil {
t.Errorf("EncodeNext(%v): %v", r, err)
}
}
2014-12-21 00:15:59 -05:00
if got := buf.String(); got != c.want {
t.Errorf("EncodeNext(%v): got %s, want %s", rows, got, c.want)
}
}
}
2014-07-17 16:32:23 -04:00
func TestEncode_Map(t *testing.T) {
for _, c := range []struct {
rows []map[string]interface{}
2014-12-21 00:15:59 -05:00
want string
}{{
[]map[string]interface{}{{
"foo": "a",
"bar": true,
"baz": 1.23,
2014-12-20 23:19:02 -05:00
"ip": ip,
}, {
"foo": "b",
"bar": false,
"baz": 4.56,
2014-12-20 23:19:02 -05:00
"ip": ip,
}},
// Keys are sorted before being written to the header
2014-12-20 23:19:02 -05:00
`bar,baz,foo,ip
true,1.23,a,128.0.0.1
false,4.56,b,128.0.0.1
`,
}, {
[]map[string]interface{}{{
"foo": "a",
}, {
"bar": "b",
}},
`foo
a
`,
}, {
[]map[string]interface{}{{
"foo": "",
}, {
"foo": true,
}},
`foo
true
`,
}} {
var buf bytes.Buffer
e := NewEncoder(&buf)
for _, r := range c.rows {
if err := e.EncodeNext(r); err != nil {
t.Errorf("EncodeNext(%v): %v", r, err)
}
}
got := buf.String()
if backcompat {
got = strings.Replace(got, `""`, "", -1)
}
if got != c.want {
t.Errorf("EncodeNext(%v): got %s, want %s", c.rows, got, c.want)
}
}
}
2014-07-17 16:32:23 -04:00
// Tests that encoding a struct then encoding a compatible map works as expected.
func TestEncode_Hybrid(t *testing.T) {
2014-07-17 16:32:23 -04:00
var buf bytes.Buffer
e := NewEncoder(&buf)
s := struct {
Foo string `csv:"foo"`
Bar string
}{"a", "b"}
if err := e.EncodeNext(s); err != nil {
t.Errorf("EncodeNext(%v): %v", r, err)
2014-07-17 16:32:23 -04:00
}
m := map[string]interface{}{
"foo": "c",
"Bar": "d",
}
if err := e.EncodeNext(m); err != nil {
t.Errorf("EncodeNext(%v): %v", r, err)
}
2014-12-21 00:15:59 -05:00
want := `foo,Bar
a,b
c,d
`
2014-12-21 00:15:59 -05:00
if got := buf.String(); got != want {
t.Errorf("EncodeNext(%v): got %s, want %s", m, got, want)
}
}
// Tests that values that implement encoding.TextMarshaler are correctly marshaled.
func TestEncode_TextMarshaler(t *testing.T) {
var buf bytes.Buffer
e := NewEncoder(&buf)
r := struct{ N net.IP }{ip}
if err := e.EncodeNext(r); err != nil {
t.Errorf("EncodeNext(%v): %v", r, err)
}
2014-12-21 00:15:59 -05:00
want := `N
2014-12-20 23:10:34 -05:00
128.0.0.1
`
2014-12-21 00:15:59 -05:00
if got := buf.String(); got != want {
t.Errorf("EncodeNext(%v): got %s, want %s", r, got, want)
2014-12-21 00:15:59 -05:00
}
}
// Tests that structs with pointer fields are encoded correctly.
func TestEncode_Ptrs(t *testing.T) {
var buf bytes.Buffer
e := NewEncoder(&buf)
bar := "bar"
s := struct {
S string
SP *string
}{bar, &bar}
if err := e.EncodeNext(s); err != nil {
t.Errorf("EncodeNext(%v): %v", r, err)
2014-12-21 00:15:59 -05:00
}
want := `S,SP
bar,bar
`
if got := buf.String(); got != want {
t.Errorf("EncodeNext(%v): got %s, want %s", s, got, want)
2014-07-17 16:32:23 -04:00
}
}