mirror of
https://github.com/tailscale/tailscale.git
synced 2025-09-21 21:51:21 +02:00
This adds support for having every viewer type implement jsonv2.MarshalerTo and jsonv2.UnmarshalerFrom. This provides a significant boost in performance as the json package no longer needs to validate the entirety of the JSON value outputted by MarshalJSON, nor does it need to identify the boundaries of a JSON value in order to call UnmarshalJSON. For deeply nested and recursive MarshalJSON or UnmarshalJSON calls, this can improve runtime from O(N²) to O(N). This still references "github.com/go-json-experiment/json" instead of the experimental "encoding/json/v2" package now available in Go 1.25 under goexperiment.jsonv2 so that code still builds without the experiment tag. Of note, the "github.com/go-json-experiment/json" package aliases the standard library under the right build conditions. Updates tailscale/corp#791 Signed-off-by: Joe Tsai <joetsai@digital-static.net>
79 lines
1.7 KiB
Go
79 lines
1.7 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"go/types"
|
|
"testing"
|
|
|
|
"tailscale.com/util/codegen"
|
|
)
|
|
|
|
func TestViewerImports(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
content string
|
|
typeNames []string
|
|
wantImports [][2]string
|
|
}{
|
|
{
|
|
name: "Map",
|
|
content: `type Test struct { Map map[string]int }`,
|
|
typeNames: []string{"Test"},
|
|
wantImports: [][2]string{{"", "tailscale.com/types/views"}},
|
|
},
|
|
{
|
|
name: "Slice",
|
|
content: `type Test struct { Slice []int }`,
|
|
typeNames: []string{"Test"},
|
|
wantImports: [][2]string{{"", "tailscale.com/types/views"}},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
fset := token.NewFileSet()
|
|
f, err := parser.ParseFile(fset, "test.go", "package test\n\n"+tt.content, 0)
|
|
if err != nil {
|
|
fmt.Println("Error parsing:", err)
|
|
return
|
|
}
|
|
|
|
info := &types.Info{
|
|
Types: make(map[ast.Expr]types.TypeAndValue),
|
|
}
|
|
|
|
conf := types.Config{}
|
|
pkg, err := conf.Check("", fset, []*ast.File{f}, info)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var output bytes.Buffer
|
|
tracker := codegen.NewImportTracker(pkg)
|
|
for i := range tt.typeNames {
|
|
typeName, ok := pkg.Scope().Lookup(tt.typeNames[i]).(*types.TypeName)
|
|
if !ok {
|
|
t.Fatalf("type %q does not exist", tt.typeNames[i])
|
|
}
|
|
namedType, ok := typeName.Type().(*types.Named)
|
|
if !ok {
|
|
t.Fatalf("%q is not a named type", tt.typeNames[i])
|
|
}
|
|
genView(&output, tracker, namedType, pkg)
|
|
}
|
|
|
|
for _, pkg := range tt.wantImports {
|
|
if !tracker.Has(pkg[0], pkg[1]) {
|
|
t.Errorf("missing import %q", pkg)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|