github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/internal/typesinternal/errorcode_test.go (about)

     1  // Copyright 2022 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 typesinternal_test
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/constant"
    11  	"go/parser"
    12  	"go/token"
    13  	"go/types"
    14  	"path/filepath"
    15  	"runtime"
    16  	"sort"
    17  	"strings"
    18  	"testing"
    19  )
    20  
    21  func TestErrorCodes(t *testing.T) {
    22  	t.Skip("unskip this test to verify the correctness of errorcode.go for the current Go version")
    23  
    24  	// For older go versions, this file was src/go/types/errorcodes.go.
    25  	stdPath := filepath.Join(runtime.GOROOT(), "src", "internal", "types", "errors", "codes.go")
    26  	stdCodes, err := loadCodes(stdPath)
    27  	if err != nil {
    28  		t.Fatalf("loading std codes: %v", err)
    29  	}
    30  
    31  	localPath := "errorcode.go"
    32  	localCodes, err := loadCodes(localPath)
    33  	if err != nil {
    34  		t.Fatalf("loading local codes: %v", err)
    35  	}
    36  
    37  	// Verify that all std codes are present, with the correct value.
    38  	type codeVal struct {
    39  		Name  string
    40  		Value int64
    41  	}
    42  	var byValue []codeVal
    43  	for k, v := range stdCodes {
    44  		byValue = append(byValue, codeVal{k, v})
    45  	}
    46  	sort.Slice(byValue, func(i, j int) bool {
    47  		return byValue[i].Value < byValue[j].Value
    48  	})
    49  
    50  	localLookup := make(map[int64]string)
    51  	for k, v := range localCodes {
    52  		if _, ok := localLookup[v]; ok {
    53  			t.Errorf("duplicate error code value %d", v)
    54  		}
    55  		localLookup[v] = k
    56  	}
    57  
    58  	for _, std := range byValue {
    59  		local, ok := localCodes[std.Name]
    60  		if !ok {
    61  			if v, ok := localLookup[std.Value]; ok {
    62  				t.Errorf("Missing code for %s (code %d is %s)", std.Name, std.Value, v)
    63  			} else {
    64  				t.Errorf("Missing code for %s", std.Name)
    65  			}
    66  		}
    67  		if local != std.Value {
    68  			t.Errorf("Mismatching value for %s: got %d, but stdlib has %d", std.Name, local, std.Value)
    69  		}
    70  	}
    71  }
    72  
    73  // loadCodes loads all constant values found in filepath.
    74  //
    75  // The given file must type-check cleanly as a standalone file.
    76  func loadCodes(filepath string) (map[string]int64, error) {
    77  	fset := token.NewFileSet()
    78  	f, err := parser.ParseFile(fset, filepath, nil, 0)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	var config types.Config
    83  	pkg, err := config.Check("p", fset, []*ast.File{f}, nil)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	codes := make(map[string]int64)
    89  	for _, name := range pkg.Scope().Names() {
    90  		obj := pkg.Scope().Lookup(name)
    91  		c, ok := obj.(*types.Const)
    92  		if !ok {
    93  			continue
    94  		}
    95  		name := strings.TrimPrefix(name, "_") // compatibility with earlier go versions
    96  		codes[name], ok = constant.Int64Val(c.Val())
    97  		if !ok {
    98  			return nil, fmt.Errorf("non integral value %v for %s", c.Val(), name)
    99  		}
   100  	}
   101  	if len(codes) < 100 {
   102  		return nil, fmt.Errorf("sanity check: got %d codes but expected at least 100", len(codes))
   103  	}
   104  	return codes, nil
   105  }