| // Copyright 2021 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package fuzz |
| |
| import ( |
| "strconv" |
| "strings" |
| "testing" |
| ) |
| |
| func TestUnmarshalMarshal(t *testing.T) { |
| var tests = []struct { |
| in string |
| ok bool |
| }{ |
| { |
| in: "int(1234)", |
| ok: false, // missing version |
| }, |
| { |
| in: `go test fuzz v1 |
| string("a"bcad")`, |
| ok: false, // malformed |
| }, |
| { |
| in: `go test fuzz v1 |
| int()`, |
| ok: false, // empty value |
| }, |
| { |
| in: `go test fuzz v1 |
| uint(-32)`, |
| ok: false, // invalid negative uint |
| }, |
| { |
| in: `go test fuzz v1 |
| int8(1234456)`, |
| ok: false, // int8 too large |
| }, |
| { |
| in: `go test fuzz v1 |
| int(20*5)`, |
| ok: false, // expression in int value |
| }, |
| { |
| in: `go test fuzz v1 |
| int(--5)`, |
| ok: false, // expression in int value |
| }, |
| { |
| in: `go test fuzz v1 |
| bool(0)`, |
| ok: false, // malformed bool |
| }, |
| { |
| in: `go test fuzz v1 |
| byte('aa)`, |
| ok: false, // malformed byte |
| }, |
| { |
| in: `go test fuzz v1 |
| byte('☃')`, |
| ok: false, // byte out of range |
| }, |
| { |
| in: `go test fuzz v1 |
| string("has final newline") |
| `, |
| ok: true, // has final newline |
| }, |
| { |
| in: `go test fuzz v1 |
| string("extra") |
| []byte("spacing") |
| `, |
| ok: true, // extra spaces in the final newline |
| }, |
| { |
| in: `go test fuzz v1 |
| float64(0) |
| float32(0)`, |
| ok: true, // will be an integer literal since there is no decimal |
| }, |
| { |
| in: `go test fuzz v1 |
| int(-23) |
| int8(-2) |
| int64(2342425) |
| uint(1) |
| uint16(234) |
| uint32(352342) |
| uint64(123) |
| rune('œ') |
| byte('K') |
| byte('ÿ') |
| []byte("hello¿") |
| []byte("a") |
| bool(true) |
| string("hello\\xbd\\xb2=\\xbc ⌘") |
| float64(-12.5) |
| float32(2.5)`, |
| ok: true, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.in, func(t *testing.T) { |
| vals, err := unmarshalCorpusFile([]byte(test.in)) |
| if test.ok && err != nil { |
| t.Fatalf("unmarshal unexpected error: %v", err) |
| } else if !test.ok && err == nil { |
| t.Fatalf("unmarshal unexpected success") |
| } |
| if !test.ok { |
| return // skip the rest of the test |
| } |
| newB := marshalCorpusFile(vals...) |
| if err != nil { |
| t.Fatalf("marshal unexpected error: %v", err) |
| } |
| if newB[len(newB)-1] != '\n' { |
| t.Error("didn't write final newline to corpus file") |
| } |
| before, after := strings.TrimSpace(test.in), strings.TrimSpace(string(newB)) |
| if before != after { |
| t.Errorf("values changed after unmarshal then marshal\nbefore: %q\nafter: %q", before, after) |
| } |
| }) |
| } |
| } |
| |
| // BenchmarkMarshalCorpusFile measures the time it takes to serialize byte |
| // slices of various sizes to a corpus file. The slice contains a repeating |
| // sequence of bytes 0-255 to mix escaped and non-escaped characters. |
| func BenchmarkMarshalCorpusFile(b *testing.B) { |
| buf := make([]byte, 1024*1024) |
| for i := 0; i < len(buf); i++ { |
| buf[i] = byte(i) |
| } |
| |
| for sz := 1; sz <= len(buf); sz <<= 1 { |
| sz := sz |
| b.Run(strconv.Itoa(sz), func(b *testing.B) { |
| for i := 0; i < b.N; i++ { |
| b.SetBytes(int64(sz)) |
| marshalCorpusFile(buf[:sz]) |
| } |
| }) |
| } |
| } |
| |
| // BenchmarkUnmarshalCorpusfile measures the time it takes to deserialize |
| // files encoding byte slices of various sizes. The slice contains a repeating |
| // sequence of bytes 0-255 to mix escaped and non-escaped characters. |
| func BenchmarkUnmarshalCorpusFile(b *testing.B) { |
| buf := make([]byte, 1024*1024) |
| for i := 0; i < len(buf); i++ { |
| buf[i] = byte(i) |
| } |
| |
| for sz := 1; sz <= len(buf); sz <<= 1 { |
| sz := sz |
| data := marshalCorpusFile(buf[:sz]) |
| b.Run(strconv.Itoa(sz), func(b *testing.B) { |
| for i := 0; i < b.N; i++ { |
| b.SetBytes(int64(sz)) |
| unmarshalCorpusFile(data) |
| } |
| }) |
| } |
| } |