• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 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
5package reflectlite_test
6
7import (
8	"fmt"
9	"go/ast"
10	"go/parser"
11	"go/token"
12	"io/fs"
13	"os"
14	"path/filepath"
15	"runtime"
16	"strings"
17	"sync"
18	"testing"
19)
20
21var typeNames = []string{
22	"uncommonType",
23	"arrayType",
24	"chanType",
25	"funcType",
26	"interfaceType",
27	"ptrType",
28	"sliceType",
29	"structType",
30}
31
32type visitor struct {
33	m map[string]map[string]bool
34}
35
36func newVisitor() visitor {
37	v := visitor{}
38	v.m = make(map[string]map[string]bool)
39
40	return v
41}
42func (v visitor) filter(name string) bool {
43	for _, typeName := range typeNames {
44		if typeName == name {
45			return true
46		}
47	}
48	return false
49}
50
51func (v visitor) Visit(n ast.Node) ast.Visitor {
52	switch x := n.(type) {
53	case *ast.TypeSpec:
54		if v.filter(x.Name.String()) {
55			if st, ok := x.Type.(*ast.StructType); ok {
56				v.m[x.Name.String()] = make(map[string]bool)
57				for _, field := range st.Fields.List {
58					k := fmt.Sprintf("%s", field.Type)
59					if len(field.Names) > 0 {
60						k = field.Names[0].Name
61					}
62					v.m[x.Name.String()][k] = true
63				}
64			}
65		}
66	}
67	return v
68}
69
70func loadTypes(path, pkgName string, v visitor) {
71	fset := token.NewFileSet()
72
73	filter := func(fi fs.FileInfo) bool {
74		return strings.HasSuffix(fi.Name(), ".go")
75	}
76	pkgs, err := parser.ParseDir(fset, path, filter, 0)
77	if err != nil {
78		panic(err)
79	}
80
81	pkg := pkgs[pkgName]
82
83	for _, f := range pkg.Files {
84		ast.Walk(v, f)
85	}
86}
87
88func TestMirrorWithReflect(t *testing.T) {
89	// TODO when the dust clears, figure out what this should actually test.
90	t.Skipf("reflect and reflectlite are out of sync for now")
91	reflectDir := filepath.Join(runtime.GOROOT(), "src", "reflect")
92	if _, err := os.Stat(reflectDir); os.IsNotExist(err) {
93		// On some mobile builders, the test binary executes on a machine without a
94		// complete GOROOT source tree.
95		t.Skipf("GOROOT source not present")
96	}
97
98	var wg sync.WaitGroup
99	rl, r := newVisitor(), newVisitor()
100
101	for _, tc := range []struct {
102		path, pkg string
103		v         visitor
104	}{
105		{".", "reflectlite", rl},
106		{reflectDir, "reflect", r},
107	} {
108		tc := tc
109		wg.Add(1)
110		go func() {
111			defer wg.Done()
112			loadTypes(tc.path, tc.pkg, tc.v)
113		}()
114	}
115	wg.Wait()
116
117	if len(rl.m) != len(r.m) {
118		t.Fatalf("number of types mismatch, reflect: %d, reflectlite: %d (%+v, %+v)", len(r.m), len(rl.m), r.m, rl.m)
119	}
120
121	for typName := range r.m {
122		if len(r.m[typName]) != len(rl.m[typName]) {
123			t.Errorf("type %s number of fields mismatch, reflect: %d, reflectlite: %d", typName, len(r.m[typName]), len(rl.m[typName]))
124			continue
125		}
126		for field := range r.m[typName] {
127			if _, ok := rl.m[typName][field]; !ok {
128				t.Errorf(`Field mismatch, reflect have "%s", relectlite does not.`, field)
129			}
130		}
131	}
132}
133