exercism/go/weather-forecast/weather_forecast_test.go

141 lines
3.5 KiB
Go
Raw Permalink Normal View History

2024-11-12 16:29:15 +00:00
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, ""
}