Add github testing workflow, simplify the Makefile because that was complex because of Travis. Remove the fuzzing, needs to be re-added when that works properly with go modules (it has been disabled for quite some time). Multiple builds and files have been added so these tests can all run in parallel. Our testing now tests a couple of minutes, the codeql is by far the more expensive. Move metric's naming test to test/presubmit_test.go Add longer sleep in the TestAutoAXFR. Bye bye travis! Closes: #4266 Signed-off-by: Miek Gieben <miek@miek.nl>
421 lines
9.2 KiB
Go
421 lines
9.2 KiB
Go
package test
|
|
|
|
// These tests check for meta level items, like trailing whitespace, correct file naming etc.
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"unicode"
|
|
)
|
|
|
|
func TestTrailingWhitespace(t *testing.T) {
|
|
walker := hasTrailingWhitespaceWalker{}
|
|
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 hasTrailingWhitespaceWalker struct {
|
|
Errors []error
|
|
}
|
|
|
|
func (w *hasTrailingWhitespaceWalker) walk(path string, info os.FileInfo, _ error) error {
|
|
// Only handle regular files, skip files that are executable and skip file in the
|
|
// root that start with a .
|
|
if !info.Mode().IsRegular() {
|
|
return nil
|
|
}
|
|
if info.Mode().Perm()&0111 != 0 {
|
|
return nil
|
|
}
|
|
if strings.HasPrefix(path, "../.") {
|
|
return nil
|
|
}
|
|
if strings.Contains(path, "/vendor") {
|
|
return nil
|
|
}
|
|
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
for i := 1; scanner.Scan(); i++ {
|
|
text := scanner.Text()
|
|
trimmed := strings.TrimRightFunc(text, unicode.IsSpace)
|
|
if len(text) != len(trimmed) {
|
|
absPath, _ := filepath.Abs(path)
|
|
w.Errors = append(w.Errors, fmt.Errorf("file %q has trailing whitespace at line %d, text: %q", absPath, i, text))
|
|
}
|
|
}
|
|
|
|
err = scanner.Err()
|
|
|
|
if err != nil {
|
|
absPath, _ := filepath.Abs(path)
|
|
err = fmt.Errorf("file %q: %v", absPath, err)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
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) {
|
|
l.err = fmt.Errorf("test error message %s doesn't start with an uppercase", bl.Value)
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
type hasImportTestingWalker struct {
|
|
Errors []error
|
|
}
|
|
|
|
func (w *hasImportTestingWalker) 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
|
|
}
|
|
|
|
if strings.HasSuffix(path, ".go") {
|
|
fs := token.NewFileSet()
|
|
f, err := parser.ParseFile(fs, path, nil, parser.AllErrors)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, im := range f.Imports {
|
|
if im.Path.Value == `"testing"` {
|
|
absPath, _ := filepath.Abs(path)
|
|
w.Errors = append(w.Errors, fmt.Errorf("file %q is importing %q", absPath, "testing"))
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
for _, im := range f.Imports {
|
|
line := fs.Position(im.Path.Pos()).Line
|
|
if line-prevpos > 1 && prevpos > 0 {
|
|
bl++
|
|
}
|
|
if bl > 2 {
|
|
absPath, _ := filepath.Abs(path)
|
|
w.Errors = append(w.Errors, fmt.Errorf("more than %d import blocks in %q", bl, absPath))
|
|
}
|
|
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 the 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"
|
|
}
|
|
|
|
// TestMetricNaming 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)
|
|
}
|
|
}
|