// +build gofuzz package logfmt import ( "bufio" "bytes" "fmt" "io" "reflect" kr "github.com/kr/logfmt" ) // Fuzz checks reserialized data matches func Fuzz(data []byte) int { parsed, err := parse(data) if err != nil { return 0 } var w1 bytes.Buffer if err = write(parsed, &w1); err != nil { panic(err) } parsed, err = parse(w1.Bytes()) if err != nil { panic(err) } var w2 bytes.Buffer if err = write(parsed, &w2); err != nil { panic(err) } if !bytes.Equal(w1.Bytes(), w2.Bytes()) { panic(fmt.Sprintf("reserialized data does not match:\n%q\n%q\n", w1.Bytes(), w2.Bytes())) } return 1 } // FuzzVsKR checks go-logfmt/logfmt against kr/logfmt func FuzzVsKR(data []byte) int { parsed, err := parse(data) parsedKR, errKR := parseKR(data) // github.com/go-logfmt/logfmt is a stricter parser. It returns errors for // more inputs than github.com/kr/logfmt. Ignore any inputs that have a // stict error. if err != nil { return 0 } // Fail if the more forgiving parser finds an error not found by the // stricter parser. if errKR != nil { panic(fmt.Sprintf("unmatched error: %v", errKR)) } if !reflect.DeepEqual(parsed, parsedKR) { panic(fmt.Sprintf("parsers disagree:\n%+v\n%+v\n", parsed, parsedKR)) } return 1 } type kv struct { k, v []byte } func parse(data []byte) ([][]kv, error) { var got [][]kv dec := NewDecoder(bytes.NewReader(data)) for dec.ScanRecord() { var kvs []kv for dec.ScanKeyval() { kvs = append(kvs, kv{dec.Key(), dec.Value()}) } got = append(got, kvs) } return got, dec.Err() } func parseKR(data []byte) ([][]kv, error) { var ( s = bufio.NewScanner(bytes.NewReader(data)) err error h saveHandler got [][]kv ) for err == nil && s.Scan() { h.kvs = nil err = kr.Unmarshal(s.Bytes(), &h) got = append(got, h.kvs) } if err == nil { err = s.Err() } return got, err } type saveHandler struct { kvs []kv } func (h *saveHandler) HandleLogfmt(key, val []byte) error { if len(key) == 0 { key = nil } if len(val) == 0 { val = nil } h.kvs = append(h.kvs, kv{key, val}) return nil } func write(recs [][]kv, w io.Writer) error { enc := NewEncoder(w) for _, rec := range recs { for _, f := range rec { if err := enc.EncodeKeyval(f.k, f.v); err != nil { return err } } if err := enc.EndRecord(); err != nil { return err } } return nil }