golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/qlog/json_writer_test.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build go1.21 6 7 package qlog 8 9 import ( 10 "bytes" 11 "errors" 12 "fmt" 13 "log/slog" 14 "strings" 15 "sync" 16 "testing" 17 "time" 18 ) 19 20 type testJSONOut struct { 21 bytes.Buffer 22 } 23 24 func (o *testJSONOut) Close() error { return nil } 25 26 func newTestJSONWriter() *jsonWriter { 27 return &jsonWriter{w: &testJSONOut{}} 28 } 29 30 func wantJSONRecord(t *testing.T, w *jsonWriter, want string) { 31 t.Helper() 32 want = "\x1e" + want + "\n" 33 got := w.w.(*testJSONOut).String() 34 if got != want { 35 t.Errorf("jsonWriter contains unexpected output\ngot: %q\nwant: %q", got, want) 36 } 37 } 38 39 func TestJSONWriterWriteConcurrentRecords(t *testing.T) { 40 w := newTestJSONWriter() 41 var wg sync.WaitGroup 42 for i := 0; i < 3; i++ { 43 wg.Add(1) 44 go func() { 45 defer wg.Done() 46 w.writeRecordStart() 47 w.writeInt64Field("field", 0) 48 w.writeRecordEnd() 49 }() 50 } 51 wg.Wait() 52 wantJSONRecord(t, w, strings.Join([]string{ 53 `{"field":0}`, 54 `{"field":0}`, 55 `{"field":0}`, 56 }, "\n\x1e")) 57 } 58 59 func TestJSONWriterAttrs(t *testing.T) { 60 w := newTestJSONWriter() 61 w.writeRecordStart() 62 w.writeAttrsField("field", []slog.Attr{ 63 slog.Any("any", errors.New("value")), 64 slog.Bool("bool", true), 65 slog.Duration("duration", 1*time.Second), 66 slog.Float64("float64", 1), 67 slog.Int64("int64", 1), 68 slog.String("string", "value"), 69 slog.Time("time", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)), 70 slog.Uint64("uint64", 1), 71 slog.Group("group", "a", 1), 72 }) 73 w.writeRecordEnd() 74 wantJSONRecord(t, w, 75 `{"field":{`+ 76 `"any":"value",`+ 77 `"bool":true,`+ 78 `"duration":1000.000000,`+ 79 `"float64":1,`+ 80 `"int64":1,`+ 81 `"string":"value",`+ 82 `"time":946684800000.000000,`+ 83 `"uint64":1,`+ 84 `"group":{"a":1}`+ 85 `}}`) 86 } 87 88 func TestJSONWriterAttrEmpty(t *testing.T) { 89 w := newTestJSONWriter() 90 w.writeRecordStart() 91 var a slog.Attr 92 w.writeAttr(a) 93 w.writeRecordEnd() 94 wantJSONRecord(t, w, `{}`) 95 } 96 97 func TestJSONWriterObjectEmpty(t *testing.T) { 98 w := newTestJSONWriter() 99 w.writeRecordStart() 100 w.writeObjectField("field", func() {}) 101 w.writeRecordEnd() 102 wantJSONRecord(t, w, `{"field":{}}`) 103 } 104 105 func TestJSONWriterObjectFields(t *testing.T) { 106 w := newTestJSONWriter() 107 w.writeRecordStart() 108 w.writeObjectField("field", func() { 109 w.writeStringField("a", "value") 110 w.writeInt64Field("b", 10) 111 }) 112 w.writeRecordEnd() 113 wantJSONRecord(t, w, `{"field":{"a":"value","b":10}}`) 114 } 115 116 func TestJSONWriterRawField(t *testing.T) { 117 w := newTestJSONWriter() 118 w.writeRecordStart() 119 w.writeRawField("field", `[1]`) 120 w.writeRecordEnd() 121 wantJSONRecord(t, w, `{"field":[1]}`) 122 } 123 124 func TestJSONWriterBoolField(t *testing.T) { 125 w := newTestJSONWriter() 126 w.writeRecordStart() 127 w.writeBoolField("true", true) 128 w.writeBoolField("false", false) 129 w.writeRecordEnd() 130 wantJSONRecord(t, w, `{"true":true,"false":false}`) 131 } 132 133 func TestJSONWriterDurationField(t *testing.T) { 134 w := newTestJSONWriter() 135 w.writeRecordStart() 136 w.writeDurationField("field1", (10*time.Millisecond)+(2*time.Nanosecond)) 137 w.writeDurationField("field2", -((10 * time.Millisecond) + (2 * time.Nanosecond))) 138 w.writeRecordEnd() 139 wantJSONRecord(t, w, `{"field1":10.000002,"field2":-10.000002}`) 140 } 141 142 func TestJSONWriterFloat64Field(t *testing.T) { 143 w := newTestJSONWriter() 144 w.writeRecordStart() 145 w.writeFloat64Field("field", 1.1) 146 w.writeRecordEnd() 147 wantJSONRecord(t, w, `{"field":1.1}`) 148 } 149 150 func TestJSONWriterInt64Field(t *testing.T) { 151 w := newTestJSONWriter() 152 w.writeRecordStart() 153 w.writeInt64Field("field", 1234) 154 w.writeRecordEnd() 155 wantJSONRecord(t, w, `{"field":1234}`) 156 } 157 158 func TestJSONWriterUint64Field(t *testing.T) { 159 w := newTestJSONWriter() 160 w.writeRecordStart() 161 w.writeUint64Field("field", 1234) 162 w.writeRecordEnd() 163 wantJSONRecord(t, w, `{"field":1234}`) 164 } 165 166 func TestJSONWriterStringField(t *testing.T) { 167 w := newTestJSONWriter() 168 w.writeRecordStart() 169 w.writeStringField("field", "value") 170 w.writeRecordEnd() 171 wantJSONRecord(t, w, `{"field":"value"}`) 172 } 173 174 func TestJSONWriterStringFieldEscaped(t *testing.T) { 175 w := newTestJSONWriter() 176 w.writeRecordStart() 177 w.writeStringField("field", "va\x00ue") 178 w.writeRecordEnd() 179 wantJSONRecord(t, w, `{"field":"va\u0000ue"}`) 180 } 181 182 func TestJSONWriterStringEscaping(t *testing.T) { 183 for c := 0; c <= 0xff; c++ { 184 w := newTestJSONWriter() 185 w.writeRecordStart() 186 w.writeStringField("field", string([]byte{byte(c)})) 187 w.writeRecordEnd() 188 var want string 189 if (c >= 0x20 && c <= 0x21) || (c >= 0x23 && c <= 0x5b) || (c >= 0x5d && c <= 0x7e) { 190 want = fmt.Sprintf(`%c`, c) 191 } else { 192 want = fmt.Sprintf(`\u%04x`, c) 193 } 194 wantJSONRecord(t, w, `{"field":"`+want+`"}`) 195 } 196 }