mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 10:13:14 -04:00 
			
		
		
		
	Fix a panic in presubmit test when import statements are split into >3 logical blocks (e.g., std, coredns, then third party in multiple blocks). The computed block index could exceed the fixed array bounds. Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
		
			
				
	
	
		
			334 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			334 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package test
 | |
| 
 | |
| // These tests check for meta level items, like trailing whitespace, correct file naming etc.
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"go/ast"
 | |
| 	"go/parser"
 | |
| 	"go/token"
 | |
| 	"os"
 | |
| 	"os/exec"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 	"unicode"
 | |
| )
 | |
| 
 | |
| func TestFileNameHyphen(t *testing.T) {
 | |
| 	walker := hasHyphenWalker{}
 | |
| 	err := filepath.Walk("..", walker.walk)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	if len(walker.Errors) > 0 {
 | |
| 		for _, err = range walker.Errors {
 | |
| 			t.Error(err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type hasHyphenWalker struct {
 | |
| 	Errors []error
 | |
| }
 | |
| 
 | |
| func (w *hasHyphenWalker) walk(path string, info os.FileInfo, _ error) error {
 | |
| 	// only for regular files, not starting with a . and those that are go files.
 | |
| 	if !info.Mode().IsRegular() {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if strings.HasPrefix(path, "../.") {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if strings.Contains(path, "/vendor") {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if filepath.Ext(path) != ".go" {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if strings.Index(path, "-") > 0 {
 | |
| 		absPath, _ := filepath.Abs(path)
 | |
| 		w.Errors = append(w.Errors, fmt.Errorf("file %q has a hyphen, please use underscores in file names", absPath))
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Test if error messages start with an upper case.
 | |
| func TestLowercaseLog(t *testing.T) {
 | |
| 	walker := hasLowercaseWalker{}
 | |
| 	err := filepath.Walk("..", walker.walk)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	if len(walker.Errors) > 0 {
 | |
| 		for _, err = range walker.Errors {
 | |
| 			t.Error(err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type hasLowercaseWalker struct {
 | |
| 	Errors []error
 | |
| }
 | |
| 
 | |
| func (w *hasLowercaseWalker) walk(path string, info os.FileInfo, _ error) error {
 | |
| 	// only for regular files, not starting with a . and those that are go files.
 | |
| 	if !info.Mode().IsRegular() {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if strings.HasPrefix(path, "../.") {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if strings.Contains(path, "/vendor") {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if !strings.HasSuffix(path, "_test.go") {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	fs := token.NewFileSet()
 | |
| 	f, err := parser.ParseFile(fs, path, nil, parser.AllErrors)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	l := &logfmt{}
 | |
| 	ast.Walk(l, f)
 | |
| 	if l.err != nil {
 | |
| 		w.Errors = append(w.Errors, l.err)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| type logfmt struct {
 | |
| 	err error
 | |
| }
 | |
| 
 | |
| func (l logfmt) Visit(n ast.Node) ast.Visitor {
 | |
| 	if n == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	ce, ok := n.(*ast.CallExpr)
 | |
| 	if !ok {
 | |
| 		return l
 | |
| 	}
 | |
| 	se, ok := ce.Fun.(*ast.SelectorExpr)
 | |
| 	if !ok {
 | |
| 		return l
 | |
| 	}
 | |
| 	id, ok := se.X.(*ast.Ident)
 | |
| 	if !ok {
 | |
| 		return l
 | |
| 	}
 | |
| 	if id.Name != "t" { // t *testing.T
 | |
| 		return l
 | |
| 	}
 | |
| 
 | |
| 	switch se.Sel.Name {
 | |
| 	case "Errorf":
 | |
| 	case "Logf":
 | |
| 	case "Log":
 | |
| 	case "Fatalf":
 | |
| 	case "Fatal":
 | |
| 	default:
 | |
| 		return l
 | |
| 	}
 | |
| 	// Check first arg, that should have basic lit with capital
 | |
| 	if len(ce.Args) < 1 {
 | |
| 		return l
 | |
| 	}
 | |
| 	bl, ok := ce.Args[0].(*ast.BasicLit)
 | |
| 	if !ok {
 | |
| 		return l
 | |
| 	}
 | |
| 	if bl.Kind != token.STRING {
 | |
| 		return l
 | |
| 	}
 | |
| 	if strings.HasPrefix(bl.Value, "\"%s") || strings.HasPrefix(bl.Value, "\"%d") {
 | |
| 		return l
 | |
| 	}
 | |
| 	if strings.HasPrefix(bl.Value, "\"%v") || strings.HasPrefix(bl.Value, "\"%+v") {
 | |
| 		return l
 | |
| 	}
 | |
| 	for i, u := range bl.Value {
 | |
| 		// disregard "
 | |
| 		if i == 1 && !unicode.IsUpper(u) {
 | |
| 			return nil
 | |
| 		}
 | |
| 		if i == 1 {
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	return l
 | |
| }
 | |
| 
 | |
| func TestImportTesting(t *testing.T) {
 | |
| 	walker := hasLowercaseWalker{}
 | |
| 	err := filepath.Walk("..", walker.walk)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	if len(walker.Errors) > 0 {
 | |
| 		for _, err = range walker.Errors {
 | |
| 			t.Error(err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestImportOrdering(t *testing.T) {
 | |
| 	walker := testImportOrderingWalker{}
 | |
| 	err := filepath.Walk("..", walker.walk)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	if len(walker.Errors) > 0 {
 | |
| 		for _, err = range walker.Errors {
 | |
| 			t.Error(err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type testImportOrderingWalker struct {
 | |
| 	Errors []error
 | |
| }
 | |
| 
 | |
| func (w *testImportOrderingWalker) walk(path string, info os.FileInfo, _ error) error {
 | |
| 	if !info.Mode().IsRegular() {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if strings.HasPrefix(path, "../.") {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if strings.Contains(path, "/vendor") {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if filepath.Ext(path) != ".go" {
 | |
| 		return nil
 | |
| 	}
 | |
| 	// pb files are autogenerated by protoc
 | |
| 	if strings.HasPrefix(path, "../pb/") {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	fs := token.NewFileSet()
 | |
| 	f, err := parser.ParseFile(fs, path, nil, parser.AllErrors)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if len(f.Imports) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	// 3 blocks total, if
 | |
| 	// 3 blocks: std + coredns + 3rd party
 | |
| 	// 2 blocks: std + coredns, std + 3rd party, coredns + 3rd party
 | |
| 	// 1 block: std, coredns, 3rd party
 | |
| 	// first entry in a block specifies the type (std, coredns, 3rd party)
 | |
| 	// we want: std, coredns, 3rd party
 | |
| 	// more than 3 blocks as an error
 | |
| 	blocks := [3][]*ast.ImportSpec{}
 | |
| 	prevpos := 0
 | |
| 	bl := 0
 | |
| 	reportedTooManyBlocks := false
 | |
| 	for _, im := range f.Imports {
 | |
| 		line := fs.Position(im.Path.Pos()).Line
 | |
| 		if line-prevpos > 1 && prevpos > 0 {
 | |
| 			bl++
 | |
| 		}
 | |
| 		if bl > 2 {
 | |
| 			if !reportedTooManyBlocks {
 | |
| 				absPath, _ := filepath.Abs(path)
 | |
| 				w.Errors = append(w.Errors, fmt.Errorf("more than %d import blocks in %q", bl, absPath))
 | |
| 				reportedTooManyBlocks = true
 | |
| 			}
 | |
| 			// Clamp to last valid block index to avoid out-of-bounds access
 | |
| 			bl = 2
 | |
| 		}
 | |
| 		blocks[bl] = append(blocks[bl], im)
 | |
| 		prevpos = line
 | |
| 	}
 | |
| 	// if it:
 | |
| 	// contains strings github.com/coredns -> coredns
 | |
| 	// contains dots -> 3rd
 | |
| 	// no dots -> std
 | |
| 	ip := [3]string{} // type per block, just string, either std, coredns, 3rd
 | |
| 	for i := 0; i <= bl; i++ {
 | |
| 		ip[i] = importtype(blocks[i][0].Path.Value)
 | |
| 	}
 | |
| 
 | |
| 	// Ok, now that we have the type, let's see if all members adhere to it.
 | |
| 	// After that we check if they are in the right order.
 | |
| 	for i := 0; i <= bl; i++ {
 | |
| 		for _, p := range blocks[i] {
 | |
| 			t := importtype(p.Path.Value)
 | |
| 			if t != ip[i] {
 | |
| 				absPath, _ := filepath.Abs(path)
 | |
| 				w.Errors = append(w.Errors, fmt.Errorf("import path for %s is not of the same type %q in %q", p.Path.Value, ip[i], absPath))
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// check order
 | |
| 	switch bl {
 | |
| 	case 0:
 | |
| 		// we don't care
 | |
| 	case 1:
 | |
| 		if ip[0] == "std" && ip[1] == "coredns" {
 | |
| 			break // OK
 | |
| 		}
 | |
| 		if ip[0] == "std" && ip[1] == "3rd" {
 | |
| 			break // OK
 | |
| 		}
 | |
| 		if ip[0] == "coredns" && ip[1] == "3rd" {
 | |
| 			break // OK
 | |
| 		}
 | |
| 		absPath, _ := filepath.Abs(path)
 | |
| 		w.Errors = append(w.Errors, fmt.Errorf("import path in %q are not in the right order (std -> coredns -> 3rd)", absPath))
 | |
| 	case 2:
 | |
| 		if ip[0] == "std" && ip[1] == "coredns" && ip[2] == "3rd" {
 | |
| 			break // OK
 | |
| 		}
 | |
| 		absPath, _ := filepath.Abs(path)
 | |
| 		w.Errors = append(w.Errors, fmt.Errorf("import path in %q are not in the right order (std -> coredns -> 3rd)", absPath))
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func importtype(s string) string {
 | |
| 	if strings.Contains(s, "github.com/coredns") {
 | |
| 		return "coredns"
 | |
| 	}
 | |
| 	if strings.Contains(s, ".") {
 | |
| 		return "3rd"
 | |
| 	}
 | |
| 	return "std"
 | |
| }
 | |
| 
 | |
| // TestPrometheusImports tests the imports path used for metrics. It depends on faillint to be installed: go install github.com/fatih/faillint
 | |
| func TestPrometheusImports(t *testing.T) {
 | |
| 	if _, err := exec.LookPath("faillint"); err != nil {
 | |
| 		fmt.Fprintf(os.Stderr, "Not executing TestPrometheusImports: faillint not found\n")
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// make this multiline?
 | |
| 	p := `github.com/prometheus/client_golang/prometheus.{NewCounter,NewCounterVec,NewCounterVec,NewGauge,NewGaugeVec,NewGaugeFunc,NewHistorgram,NewHistogramVec,NewSummary,NewSummaryVec}=github.com/prometheus/client_golang/prometheus/promauto.{NewCounter,NewCounterVec,NewCounterVec,NewGauge,NewGaugeVec,NewGaugeFunc,NewHistorgram,NewHistogramVec,NewSummary,NewSummaryVec}`
 | |
| 
 | |
| 	cmd := exec.Command("faillint", "-paths", p, "./...")
 | |
| 	cmd.Dir = ".."
 | |
| 	out, err := cmd.CombinedOutput()
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed: %s\n%s", err, out)
 | |
| 	}
 | |
| }
 |