141 lines
3.5 KiB
Go
141 lines
3.5 KiB
Go
|
package weather
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"go/ast"
|
||
|
"go/parser"
|
||
|
"go/token"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
func TestComments(t *testing.T) {
|
||
|
filename := "weather_forecast.go"
|
||
|
|
||
|
fs := token.NewFileSet()
|
||
|
f, err := parser.ParseFile(fs, filename, nil, parser.ParseComments)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
wantedComments := 4
|
||
|
got := len(f.Comments)
|
||
|
if got != wantedComments {
|
||
|
t.Errorf("Incorrect number of comments: got %d, want %d", got, wantedComments)
|
||
|
}
|
||
|
|
||
|
testPackageComment(t, f)
|
||
|
|
||
|
ast.Inspect(f, func(node ast.Node) bool {
|
||
|
switch n := node.(type) {
|
||
|
case *ast.GenDecl:
|
||
|
if n.Lparen.IsValid() {
|
||
|
for _, v := range n.Specs {
|
||
|
testBlockIdentifierComment(t, v.(*ast.ValueSpec))
|
||
|
}
|
||
|
} else {
|
||
|
testIdentifierComment(t, n)
|
||
|
}
|
||
|
case *ast.FuncDecl:
|
||
|
testFunctionComment(t, n)
|
||
|
}
|
||
|
return true
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func testPackageComment(t *testing.T, node *ast.File) {
|
||
|
t.Helper()
|
||
|
if node.Doc == nil {
|
||
|
t.Errorf("Package weather should have a comment")
|
||
|
}
|
||
|
|
||
|
packageName := node.Name.Name
|
||
|
want := "Package " + packageName
|
||
|
packageComment := node.Doc.Text()
|
||
|
|
||
|
if ok, errStr := testComment("Package", packageName, packageComment, want); !ok {
|
||
|
t.Error(errStr)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
func testIdentifierComment(t *testing.T, node *ast.GenDecl) {
|
||
|
t.Helper()
|
||
|
|
||
|
identifierName := node.Specs[0].(*ast.ValueSpec).Names[0].Name
|
||
|
if node.Doc == nil {
|
||
|
t.Errorf("Exported identifier %s should have a comment", identifierName)
|
||
|
}
|
||
|
|
||
|
identifierComment := node.Doc.Text()
|
||
|
want := identifierName
|
||
|
|
||
|
if ok, errStr := testComment("Variable", identifierName, identifierComment, want); !ok {
|
||
|
t.Error(errStr)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func testBlockIdentifierComment(t *testing.T, node *ast.ValueSpec) {
|
||
|
t.Helper()
|
||
|
|
||
|
identifierName := node.Names[0].Name
|
||
|
if node.Doc == nil {
|
||
|
t.Errorf("Exported identifier %s should have a comment", identifierName)
|
||
|
}
|
||
|
|
||
|
identifierComment := node.Doc.Text()
|
||
|
want := identifierName
|
||
|
|
||
|
if ok, errStr := testComment("Variable", identifierName, identifierComment, want); !ok {
|
||
|
t.Error(errStr)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
func testFunctionComment(t *testing.T, node *ast.FuncDecl) {
|
||
|
t.Helper()
|
||
|
funcName := node.Name.Name
|
||
|
if node.Doc == nil {
|
||
|
t.Errorf("Exported function %s() should have a comment", funcName)
|
||
|
}
|
||
|
|
||
|
funcComment := node.Doc.Text()
|
||
|
want := funcName
|
||
|
|
||
|
if ok, errStr := testComment("Function", funcName, funcComment, want); !ok {
|
||
|
t.Error(errStr)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func testComment(entityKind, entityName, comment, wantedPrefix string) (ok bool, errString string) {
|
||
|
|
||
|
trimmedComment := strings.TrimSpace(comment)
|
||
|
lowerEntity := strings.ToLower(entityKind)
|
||
|
|
||
|
// Check if comment has wanted prefix
|
||
|
if !strings.HasPrefix(trimmedComment, wantedPrefix) {
|
||
|
errorString := fmt.Sprintf("%s comment for %s '%s' should start with '// %s ...': got '// %s'",
|
||
|
entityKind, lowerEntity, entityName, wantedPrefix, trimmedComment)
|
||
|
return false, errorString
|
||
|
}
|
||
|
|
||
|
// Check if comment content is empty
|
||
|
commentContent := strings.TrimPrefix(trimmedComment, wantedPrefix)
|
||
|
commentContent = strings.TrimSpace(commentContent)
|
||
|
commentContent = strings.TrimSuffix(commentContent, ".")
|
||
|
|
||
|
if commentContent == "" {
|
||
|
lowerEntity := strings.ToLower(entityKind)
|
||
|
errorString := fmt.Sprintf("%s comment of '%s' should provide a description of the %s, e.g '// %s <%s_description>'",
|
||
|
entityKind, entityName, lowerEntity, wantedPrefix, lowerEntity)
|
||
|
return false, errorString
|
||
|
}
|
||
|
|
||
|
// Check if comment ends in a period
|
||
|
if !strings.HasSuffix(trimmedComment, ".") {
|
||
|
return false, fmt.Sprintf("%s comment for %s '%s' should end with a period (.)",
|
||
|
entityKind, lowerEntity, entityName)
|
||
|
}
|
||
|
|
||
|
return true, ""
|
||
|
}
|