From 0db2d74de8eca35d091efe4738013489c9608636 Mon Sep 17 00:00:00 2001 From: Tim Hockin Date: Tue, 27 Jun 2017 21:09:54 +0100 Subject: [PATCH] Add output test for manual json marshalers --- .../json_marshal/string_alias/json_test.go | 152 ++++++++++++++++++ .../json_marshal/string_alias/types.go | 50 ++++++ output_tests/json_marshal/struct/json_test.go | 152 ++++++++++++++++++ output_tests/json_marshal/struct/types.go | 52 ++++++ .../json_marshal/struct_alias/json_test.go | 152 ++++++++++++++++++ .../json_marshal/struct_alias/types.go | 54 +++++++ .../json_marshal/struct_field/json_test.go | 152 ++++++++++++++++++ .../json_marshal/struct_field/types.go | 56 +++++++ .../struct_field_alias/json_test.go | 152 ++++++++++++++++++ .../json_marshal/struct_field_alias/types.go | 58 +++++++ 10 files changed, 1030 insertions(+) create mode 100644 output_tests/json_marshal/string_alias/json_test.go create mode 100644 output_tests/json_marshal/string_alias/types.go create mode 100644 output_tests/json_marshal/struct/json_test.go create mode 100644 output_tests/json_marshal/struct/types.go create mode 100644 output_tests/json_marshal/struct_alias/json_test.go create mode 100644 output_tests/json_marshal/struct_alias/types.go create mode 100644 output_tests/json_marshal/struct_field/json_test.go create mode 100644 output_tests/json_marshal/struct_field/types.go create mode 100644 output_tests/json_marshal/struct_field_alias/json_test.go create mode 100644 output_tests/json_marshal/struct_field_alias/types.go diff --git a/output_tests/json_marshal/string_alias/json_test.go b/output_tests/json_marshal/string_alias/json_test.go new file mode 100644 index 0000000..8c7d795 --- /dev/null +++ b/output_tests/json_marshal/string_alias/json_test.go @@ -0,0 +1,152 @@ +package test + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/davecgh/go-spew/spew" + fuzz "github.com/google/gofuzz" + jsoniter "github.com/json-iterator/go" +) + +func Test_Roundtrip(t *testing.T) { + fz := fuzz.New().MaxDepth(10).NilChance(0.3) + for i := 0; i < 1000; i++ { + var before T + fz.Fuzz(&before) + + jbStd, err := json.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal with stdlib: %v", err) + } + if len(strings.TrimSpace(string(jbStd))) == 0 { + t.Fatal("stdlib marshal produced empty result and no error") + } + jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal with jsoniter: %v", err) + } + if len(strings.TrimSpace(string(jbIter))) == 0 { + t.Fatal("jsoniter marshal produced empty result and no error") + } + if string(jbStd) != string(jbIter) { + t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s", + indent(jbStd, " "), indent(jbIter, " "), dump(before)) + } + + var afterStd T + err = json.Unmarshal(jbIter, &afterStd) + if err != nil { + t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s", + err, indent(jbIter, " ")) + } + var afterIter T + err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter) + if err != nil { + t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s", + err, indent(jbIter, " ")) + } + if fingerprint(afterStd) != fingerprint(afterIter) { + t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s", + dump(afterStd), dump(afterIter), indent(jbIter, " ")) + } + } +} + +const indentStr = "> " + +func fingerprint(obj interface{}) string { + c := spew.ConfigState{ + SortKeys: true, + SpewKeys: true, + } + return c.Sprintf("%v", obj) +} + +func dump(obj interface{}) string { + cfg := spew.ConfigState{ + Indent: indentStr, + } + return cfg.Sdump(obj) +} + +func indent(src []byte, prefix string) string { + var buf bytes.Buffer + err := json.Indent(&buf, src, prefix, indentStr) + if err != nil { + return fmt.Sprintf("!!! %v", err) + } + return buf.String() +} + +func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) { + t.ReportAllocs() + t.ResetTimer() + + var obj T + fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3) + fz.Fuzz(&obj) + for i := 0; i < t.N; i++ { + jb, err := fn(obj) + if err != nil { + t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err) + } + _ = jb + } +} + +func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) { + t.ReportAllocs() + t.ResetTimer() + + var before T + fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3) + fz.Fuzz(&before) + jb, err := json.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal: %v", err) + } + + for i := 0; i < t.N; i++ { + var after T + err = fn(jb, &after) + if err != nil { + t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err) + } + } +} + +func BenchmarkStandardMarshal(t *testing.B) { + benchmarkMarshal(t, "stdlib", json.Marshal) +} + +func BenchmarkStandardUnmarshal(t *testing.B) { + benchmarkUnmarshal(t, "stdlib", json.Unmarshal) +} + +func BenchmarkJSONIterMarshalFastest(t *testing.B) { + benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal) +} + +func BenchmarkJSONIterUnmarshalFastest(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal) +} + +func BenchmarkJSONIterMarshalDefault(t *testing.B) { + benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal) +} + +func BenchmarkJSONIterUnmarshalDefault(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal) +} + +func BenchmarkJSONIterMarshalCompatible(t *testing.B) { + benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal) +} + +func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal) +} diff --git a/output_tests/json_marshal/string_alias/types.go b/output_tests/json_marshal/string_alias/types.go new file mode 100644 index 0000000..cbe8143 --- /dev/null +++ b/output_tests/json_marshal/string_alias/types.go @@ -0,0 +1,50 @@ +package test + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "strings" +) + +type Marshaler string + +func encode(str string) string { + buf := bytes.Buffer{} + b64 := base64.NewEncoder(base64.StdEncoding, &buf) + if _, err := b64.Write([]byte(str)); err != nil { + panic(err) + } + if err := b64.Close(); err != nil { + panic(err) + } + return buf.String() +} + +func decode(str string) string { + if len(str) == 0 { + return "" + } + b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(str)) + bs := make([]byte, len(str)) + if n, err := b64.Read(bs); err != nil { + panic(err) + } else { + bs = bs[:n] + } + return string(bs) +} + +func (m Marshaler) MarshalJSON() ([]byte, error) { + return []byte(`"MANUAL__` + encode(string(m)) + `"`), nil +} + +func (m *Marshaler) UnmarshalJSON(text []byte) error { + *m = Marshaler(decode(strings.TrimPrefix(strings.Trim(string(text), `"`), "MANUAL__"))) + return nil +} + +var _ json.Marshaler = *new(Marshaler) +var _ json.Unmarshaler = new(Marshaler) + +type T Marshaler diff --git a/output_tests/json_marshal/struct/json_test.go b/output_tests/json_marshal/struct/json_test.go new file mode 100644 index 0000000..8c7d795 --- /dev/null +++ b/output_tests/json_marshal/struct/json_test.go @@ -0,0 +1,152 @@ +package test + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/davecgh/go-spew/spew" + fuzz "github.com/google/gofuzz" + jsoniter "github.com/json-iterator/go" +) + +func Test_Roundtrip(t *testing.T) { + fz := fuzz.New().MaxDepth(10).NilChance(0.3) + for i := 0; i < 1000; i++ { + var before T + fz.Fuzz(&before) + + jbStd, err := json.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal with stdlib: %v", err) + } + if len(strings.TrimSpace(string(jbStd))) == 0 { + t.Fatal("stdlib marshal produced empty result and no error") + } + jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal with jsoniter: %v", err) + } + if len(strings.TrimSpace(string(jbIter))) == 0 { + t.Fatal("jsoniter marshal produced empty result and no error") + } + if string(jbStd) != string(jbIter) { + t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s", + indent(jbStd, " "), indent(jbIter, " "), dump(before)) + } + + var afterStd T + err = json.Unmarshal(jbIter, &afterStd) + if err != nil { + t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s", + err, indent(jbIter, " ")) + } + var afterIter T + err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter) + if err != nil { + t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s", + err, indent(jbIter, " ")) + } + if fingerprint(afterStd) != fingerprint(afterIter) { + t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s", + dump(afterStd), dump(afterIter), indent(jbIter, " ")) + } + } +} + +const indentStr = "> " + +func fingerprint(obj interface{}) string { + c := spew.ConfigState{ + SortKeys: true, + SpewKeys: true, + } + return c.Sprintf("%v", obj) +} + +func dump(obj interface{}) string { + cfg := spew.ConfigState{ + Indent: indentStr, + } + return cfg.Sdump(obj) +} + +func indent(src []byte, prefix string) string { + var buf bytes.Buffer + err := json.Indent(&buf, src, prefix, indentStr) + if err != nil { + return fmt.Sprintf("!!! %v", err) + } + return buf.String() +} + +func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) { + t.ReportAllocs() + t.ResetTimer() + + var obj T + fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3) + fz.Fuzz(&obj) + for i := 0; i < t.N; i++ { + jb, err := fn(obj) + if err != nil { + t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err) + } + _ = jb + } +} + +func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) { + t.ReportAllocs() + t.ResetTimer() + + var before T + fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3) + fz.Fuzz(&before) + jb, err := json.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal: %v", err) + } + + for i := 0; i < t.N; i++ { + var after T + err = fn(jb, &after) + if err != nil { + t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err) + } + } +} + +func BenchmarkStandardMarshal(t *testing.B) { + benchmarkMarshal(t, "stdlib", json.Marshal) +} + +func BenchmarkStandardUnmarshal(t *testing.B) { + benchmarkUnmarshal(t, "stdlib", json.Unmarshal) +} + +func BenchmarkJSONIterMarshalFastest(t *testing.B) { + benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal) +} + +func BenchmarkJSONIterUnmarshalFastest(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal) +} + +func BenchmarkJSONIterMarshalDefault(t *testing.B) { + benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal) +} + +func BenchmarkJSONIterUnmarshalDefault(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal) +} + +func BenchmarkJSONIterMarshalCompatible(t *testing.B) { + benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal) +} + +func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal) +} diff --git a/output_tests/json_marshal/struct/types.go b/output_tests/json_marshal/struct/types.go new file mode 100644 index 0000000..f556b05 --- /dev/null +++ b/output_tests/json_marshal/struct/types.go @@ -0,0 +1,52 @@ +package test + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "strings" +) + +type Marshaler struct { + X string +} + +func encode(str string) string { + buf := bytes.Buffer{} + b64 := base64.NewEncoder(base64.StdEncoding, &buf) + if _, err := b64.Write([]byte(str)); err != nil { + panic(err) + } + if err := b64.Close(); err != nil { + panic(err) + } + return buf.String() +} + +func decode(str string) string { + if len(str) == 0 { + return "" + } + b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(str)) + bs := make([]byte, len(str)) + if n, err := b64.Read(bs); err != nil { + panic(err) + } else { + bs = bs[:n] + } + return string(bs) +} + +func (m Marshaler) MarshalJSON() ([]byte, error) { + return []byte(`"MANUAL__` + encode(m.X) + `"`), nil +} + +func (m *Marshaler) UnmarshalJSON(text []byte) error { + m.X = decode(strings.TrimPrefix(strings.Trim(string(text), `"`), "MANUAL__")) + return nil +} + +var _ json.Marshaler = Marshaler{} +var _ json.Unmarshaler = &Marshaler{} + +type T Marshaler diff --git a/output_tests/json_marshal/struct_alias/json_test.go b/output_tests/json_marshal/struct_alias/json_test.go new file mode 100644 index 0000000..8c7d795 --- /dev/null +++ b/output_tests/json_marshal/struct_alias/json_test.go @@ -0,0 +1,152 @@ +package test + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/davecgh/go-spew/spew" + fuzz "github.com/google/gofuzz" + jsoniter "github.com/json-iterator/go" +) + +func Test_Roundtrip(t *testing.T) { + fz := fuzz.New().MaxDepth(10).NilChance(0.3) + for i := 0; i < 1000; i++ { + var before T + fz.Fuzz(&before) + + jbStd, err := json.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal with stdlib: %v", err) + } + if len(strings.TrimSpace(string(jbStd))) == 0 { + t.Fatal("stdlib marshal produced empty result and no error") + } + jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal with jsoniter: %v", err) + } + if len(strings.TrimSpace(string(jbIter))) == 0 { + t.Fatal("jsoniter marshal produced empty result and no error") + } + if string(jbStd) != string(jbIter) { + t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s", + indent(jbStd, " "), indent(jbIter, " "), dump(before)) + } + + var afterStd T + err = json.Unmarshal(jbIter, &afterStd) + if err != nil { + t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s", + err, indent(jbIter, " ")) + } + var afterIter T + err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter) + if err != nil { + t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s", + err, indent(jbIter, " ")) + } + if fingerprint(afterStd) != fingerprint(afterIter) { + t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s", + dump(afterStd), dump(afterIter), indent(jbIter, " ")) + } + } +} + +const indentStr = "> " + +func fingerprint(obj interface{}) string { + c := spew.ConfigState{ + SortKeys: true, + SpewKeys: true, + } + return c.Sprintf("%v", obj) +} + +func dump(obj interface{}) string { + cfg := spew.ConfigState{ + Indent: indentStr, + } + return cfg.Sdump(obj) +} + +func indent(src []byte, prefix string) string { + var buf bytes.Buffer + err := json.Indent(&buf, src, prefix, indentStr) + if err != nil { + return fmt.Sprintf("!!! %v", err) + } + return buf.String() +} + +func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) { + t.ReportAllocs() + t.ResetTimer() + + var obj T + fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3) + fz.Fuzz(&obj) + for i := 0; i < t.N; i++ { + jb, err := fn(obj) + if err != nil { + t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err) + } + _ = jb + } +} + +func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) { + t.ReportAllocs() + t.ResetTimer() + + var before T + fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3) + fz.Fuzz(&before) + jb, err := json.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal: %v", err) + } + + for i := 0; i < t.N; i++ { + var after T + err = fn(jb, &after) + if err != nil { + t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err) + } + } +} + +func BenchmarkStandardMarshal(t *testing.B) { + benchmarkMarshal(t, "stdlib", json.Marshal) +} + +func BenchmarkStandardUnmarshal(t *testing.B) { + benchmarkUnmarshal(t, "stdlib", json.Unmarshal) +} + +func BenchmarkJSONIterMarshalFastest(t *testing.B) { + benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal) +} + +func BenchmarkJSONIterUnmarshalFastest(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal) +} + +func BenchmarkJSONIterMarshalDefault(t *testing.B) { + benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal) +} + +func BenchmarkJSONIterUnmarshalDefault(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal) +} + +func BenchmarkJSONIterMarshalCompatible(t *testing.B) { + benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal) +} + +func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal) +} diff --git a/output_tests/json_marshal/struct_alias/types.go b/output_tests/json_marshal/struct_alias/types.go new file mode 100644 index 0000000..f1c7b97 --- /dev/null +++ b/output_tests/json_marshal/struct_alias/types.go @@ -0,0 +1,54 @@ +package test + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "strings" +) + +type Marshaler struct { + X string +} + +func encode(str string) string { + buf := bytes.Buffer{} + b64 := base64.NewEncoder(base64.StdEncoding, &buf) + if _, err := b64.Write([]byte(str)); err != nil { + panic(err) + } + if err := b64.Close(); err != nil { + panic(err) + } + return buf.String() +} + +func decode(str string) string { + if len(str) == 0 { + return "" + } + b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(str)) + bs := make([]byte, len(str)) + if n, err := b64.Read(bs); err != nil { + panic(err) + } else { + bs = bs[:n] + } + return string(bs) +} + +func (m Marshaler) MarshalJSON() ([]byte, error) { + return []byte(`"MANUAL__` + encode(m.X) + `"`), nil +} + +func (m *Marshaler) UnmarshalJSON(text []byte) error { + m.X = decode(strings.TrimPrefix(strings.Trim(string(text), `"`), "MANUAL__")) + return nil +} + +var _ json.Marshaler = Marshaler{} +var _ json.Unmarshaler = &Marshaler{} + +type A Marshaler + +type T A diff --git a/output_tests/json_marshal/struct_field/json_test.go b/output_tests/json_marshal/struct_field/json_test.go new file mode 100644 index 0000000..8c7d795 --- /dev/null +++ b/output_tests/json_marshal/struct_field/json_test.go @@ -0,0 +1,152 @@ +package test + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/davecgh/go-spew/spew" + fuzz "github.com/google/gofuzz" + jsoniter "github.com/json-iterator/go" +) + +func Test_Roundtrip(t *testing.T) { + fz := fuzz.New().MaxDepth(10).NilChance(0.3) + for i := 0; i < 1000; i++ { + var before T + fz.Fuzz(&before) + + jbStd, err := json.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal with stdlib: %v", err) + } + if len(strings.TrimSpace(string(jbStd))) == 0 { + t.Fatal("stdlib marshal produced empty result and no error") + } + jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal with jsoniter: %v", err) + } + if len(strings.TrimSpace(string(jbIter))) == 0 { + t.Fatal("jsoniter marshal produced empty result and no error") + } + if string(jbStd) != string(jbIter) { + t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s", + indent(jbStd, " "), indent(jbIter, " "), dump(before)) + } + + var afterStd T + err = json.Unmarshal(jbIter, &afterStd) + if err != nil { + t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s", + err, indent(jbIter, " ")) + } + var afterIter T + err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter) + if err != nil { + t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s", + err, indent(jbIter, " ")) + } + if fingerprint(afterStd) != fingerprint(afterIter) { + t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s", + dump(afterStd), dump(afterIter), indent(jbIter, " ")) + } + } +} + +const indentStr = "> " + +func fingerprint(obj interface{}) string { + c := spew.ConfigState{ + SortKeys: true, + SpewKeys: true, + } + return c.Sprintf("%v", obj) +} + +func dump(obj interface{}) string { + cfg := spew.ConfigState{ + Indent: indentStr, + } + return cfg.Sdump(obj) +} + +func indent(src []byte, prefix string) string { + var buf bytes.Buffer + err := json.Indent(&buf, src, prefix, indentStr) + if err != nil { + return fmt.Sprintf("!!! %v", err) + } + return buf.String() +} + +func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) { + t.ReportAllocs() + t.ResetTimer() + + var obj T + fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3) + fz.Fuzz(&obj) + for i := 0; i < t.N; i++ { + jb, err := fn(obj) + if err != nil { + t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err) + } + _ = jb + } +} + +func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) { + t.ReportAllocs() + t.ResetTimer() + + var before T + fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3) + fz.Fuzz(&before) + jb, err := json.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal: %v", err) + } + + for i := 0; i < t.N; i++ { + var after T + err = fn(jb, &after) + if err != nil { + t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err) + } + } +} + +func BenchmarkStandardMarshal(t *testing.B) { + benchmarkMarshal(t, "stdlib", json.Marshal) +} + +func BenchmarkStandardUnmarshal(t *testing.B) { + benchmarkUnmarshal(t, "stdlib", json.Unmarshal) +} + +func BenchmarkJSONIterMarshalFastest(t *testing.B) { + benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal) +} + +func BenchmarkJSONIterUnmarshalFastest(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal) +} + +func BenchmarkJSONIterMarshalDefault(t *testing.B) { + benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal) +} + +func BenchmarkJSONIterUnmarshalDefault(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal) +} + +func BenchmarkJSONIterMarshalCompatible(t *testing.B) { + benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal) +} + +func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal) +} diff --git a/output_tests/json_marshal/struct_field/types.go b/output_tests/json_marshal/struct_field/types.go new file mode 100644 index 0000000..517b0cd --- /dev/null +++ b/output_tests/json_marshal/struct_field/types.go @@ -0,0 +1,56 @@ +package test + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "strings" +) + +type Marshaler struct { + X string +} + +func encode(str string) string { + buf := bytes.Buffer{} + b64 := base64.NewEncoder(base64.StdEncoding, &buf) + if _, err := b64.Write([]byte(str)); err != nil { + panic(err) + } + if err := b64.Close(); err != nil { + panic(err) + } + return buf.String() +} + +func decode(str string) string { + if len(str) == 0 { + return "" + } + b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(str)) + bs := make([]byte, len(str)) + if n, err := b64.Read(bs); err != nil { + panic(err) + } else { + bs = bs[:n] + } + return string(bs) +} + +func (m Marshaler) MarshalJSON() ([]byte, error) { + return []byte(`"MANUAL__` + encode(m.X) + `"`), nil +} + +func (m *Marshaler) UnmarshalJSON(text []byte) error { + m.X = decode(strings.TrimPrefix(strings.Trim(string(text), `"`), "MANUAL__")) + return nil +} + +var _ json.Marshaler = Marshaler{} +var _ json.Unmarshaler = &Marshaler{} + +type T struct { + S string + M Marshaler + I int8 +} diff --git a/output_tests/json_marshal/struct_field_alias/json_test.go b/output_tests/json_marshal/struct_field_alias/json_test.go new file mode 100644 index 0000000..8c7d795 --- /dev/null +++ b/output_tests/json_marshal/struct_field_alias/json_test.go @@ -0,0 +1,152 @@ +package test + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/davecgh/go-spew/spew" + fuzz "github.com/google/gofuzz" + jsoniter "github.com/json-iterator/go" +) + +func Test_Roundtrip(t *testing.T) { + fz := fuzz.New().MaxDepth(10).NilChance(0.3) + for i := 0; i < 1000; i++ { + var before T + fz.Fuzz(&before) + + jbStd, err := json.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal with stdlib: %v", err) + } + if len(strings.TrimSpace(string(jbStd))) == 0 { + t.Fatal("stdlib marshal produced empty result and no error") + } + jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal with jsoniter: %v", err) + } + if len(strings.TrimSpace(string(jbIter))) == 0 { + t.Fatal("jsoniter marshal produced empty result and no error") + } + if string(jbStd) != string(jbIter) { + t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s", + indent(jbStd, " "), indent(jbIter, " "), dump(before)) + } + + var afterStd T + err = json.Unmarshal(jbIter, &afterStd) + if err != nil { + t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s", + err, indent(jbIter, " ")) + } + var afterIter T + err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter) + if err != nil { + t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s", + err, indent(jbIter, " ")) + } + if fingerprint(afterStd) != fingerprint(afterIter) { + t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s", + dump(afterStd), dump(afterIter), indent(jbIter, " ")) + } + } +} + +const indentStr = "> " + +func fingerprint(obj interface{}) string { + c := spew.ConfigState{ + SortKeys: true, + SpewKeys: true, + } + return c.Sprintf("%v", obj) +} + +func dump(obj interface{}) string { + cfg := spew.ConfigState{ + Indent: indentStr, + } + return cfg.Sdump(obj) +} + +func indent(src []byte, prefix string) string { + var buf bytes.Buffer + err := json.Indent(&buf, src, prefix, indentStr) + if err != nil { + return fmt.Sprintf("!!! %v", err) + } + return buf.String() +} + +func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) { + t.ReportAllocs() + t.ResetTimer() + + var obj T + fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3) + fz.Fuzz(&obj) + for i := 0; i < t.N; i++ { + jb, err := fn(obj) + if err != nil { + t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err) + } + _ = jb + } +} + +func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) { + t.ReportAllocs() + t.ResetTimer() + + var before T + fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3) + fz.Fuzz(&before) + jb, err := json.Marshal(before) + if err != nil { + t.Fatalf("failed to marshal: %v", err) + } + + for i := 0; i < t.N; i++ { + var after T + err = fn(jb, &after) + if err != nil { + t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err) + } + } +} + +func BenchmarkStandardMarshal(t *testing.B) { + benchmarkMarshal(t, "stdlib", json.Marshal) +} + +func BenchmarkStandardUnmarshal(t *testing.B) { + benchmarkUnmarshal(t, "stdlib", json.Unmarshal) +} + +func BenchmarkJSONIterMarshalFastest(t *testing.B) { + benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal) +} + +func BenchmarkJSONIterUnmarshalFastest(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal) +} + +func BenchmarkJSONIterMarshalDefault(t *testing.B) { + benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal) +} + +func BenchmarkJSONIterUnmarshalDefault(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal) +} + +func BenchmarkJSONIterMarshalCompatible(t *testing.B) { + benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal) +} + +func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) { + benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal) +} diff --git a/output_tests/json_marshal/struct_field_alias/types.go b/output_tests/json_marshal/struct_field_alias/types.go new file mode 100644 index 0000000..ada9135 --- /dev/null +++ b/output_tests/json_marshal/struct_field_alias/types.go @@ -0,0 +1,58 @@ +package test + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "strings" +) + +type Marshaler struct { + X string +} + +func encode(str string) string { + buf := bytes.Buffer{} + b64 := base64.NewEncoder(base64.StdEncoding, &buf) + if _, err := b64.Write([]byte(str)); err != nil { + panic(err) + } + if err := b64.Close(); err != nil { + panic(err) + } + return buf.String() +} + +func decode(str string) string { + if len(str) == 0 { + return "" + } + b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(str)) + bs := make([]byte, len(str)) + if n, err := b64.Read(bs); err != nil { + panic(err) + } else { + bs = bs[:n] + } + return string(bs) +} + +func (m Marshaler) MarshalJSON() ([]byte, error) { + return []byte(`"MANUAL__` + encode(m.X) + `"`), nil +} + +func (m *Marshaler) UnmarshalJSON(text []byte) error { + m.X = decode(strings.TrimPrefix(strings.Trim(string(text), `"`), "MANUAL__")) + return nil +} + +var _ json.Marshaler = Marshaler{} +var _ json.Unmarshaler = &Marshaler{} + +type A Marshaler + +type T struct { + S string + M A + I int8 +}