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  }