github.com/SandwichDev/go-internals@v0.0.0-20210605002614-12311ac6b2c5/reflectlite/reflect_mirror_test.go (about)

     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  
     5  package reflectlite_test
     6  
     7  import (
     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  
    21  var typeNames = []string{
    22  	"rtype",
    23  	"uncommonType",
    24  	"arrayType",
    25  	"chanType",
    26  	"funcType",
    27  	"interfaceType",
    28  	"mapType",
    29  	"ptrType",
    30  	"sliceType",
    31  	"structType",
    32  }
    33  
    34  type visitor struct {
    35  	m map[string]map[string]bool
    36  }
    37  
    38  func newVisitor() visitor {
    39  	v := visitor{}
    40  	v.m = make(map[string]map[string]bool)
    41  
    42  	return v
    43  }
    44  func (v visitor) filter(name string) bool {
    45  	for _, typeName := range typeNames {
    46  		if typeName == name {
    47  			return true
    48  		}
    49  	}
    50  	return false
    51  }
    52  
    53  func (v visitor) Visit(n ast.Node) ast.Visitor {
    54  	switch x := n.(type) {
    55  	case *ast.TypeSpec:
    56  		if v.filter(x.Name.String()) {
    57  			if st, ok := x.Type.(*ast.StructType); ok {
    58  				v.m[x.Name.String()] = make(map[string]bool)
    59  				for _, field := range st.Fields.List {
    60  					k := fmt.Sprintf("%s", field.Type)
    61  					if len(field.Names) > 0 {
    62  						k = field.Names[0].Name
    63  					}
    64  					v.m[x.Name.String()][k] = true
    65  				}
    66  			}
    67  		}
    68  	}
    69  	return v
    70  }
    71  
    72  func loadTypes(path, pkgName string, v visitor) {
    73  	fset := token.NewFileSet()
    74  
    75  	filter := func(fi fs.FileInfo) bool {
    76  		return strings.HasSuffix(fi.Name(), ".go")
    77  	}
    78  	pkgs, err := parser.ParseDir(fset, path, filter, 0)
    79  	if err != nil {
    80  		panic(err)
    81  	}
    82  
    83  	pkg := pkgs[pkgName]
    84  
    85  	for _, f := range pkg.Files {
    86  		ast.Walk(v, f)
    87  	}
    88  }
    89  
    90  func TestMirrorWithReflect(t *testing.T) {
    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", len(r.m), len(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  }