107 lines
2.8 KiB
Go
107 lines
2.8 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package sando
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"go/importer"
|
|
"go/parser"
|
|
"go/token"
|
|
"go/types"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestV1PublicAPIContract(t *testing.T) {
|
|
t.Parallel()
|
|
entries, err := os.ReadDir(".")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fileSet := token.NewFileSet()
|
|
var files []*ast.File
|
|
for _, entry := range entries {
|
|
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") || strings.HasSuffix(entry.Name(), "_test.go") {
|
|
continue
|
|
}
|
|
file, err := parser.ParseFile(fileSet, entry.Name(), nil, 0)
|
|
if err != nil {
|
|
t.Fatalf("parse %s: %v", entry.Name(), err)
|
|
}
|
|
files = append(files, file)
|
|
}
|
|
configuration := types.Config{Importer: importer.Default()}
|
|
checked, err := configuration.Check("gamertan.com/sandwich-hime/sando", fileSet, files, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
qualifier := func(pkg *types.Package) string {
|
|
if pkg == nil || pkg.Path() == checked.Path() {
|
|
return ""
|
|
}
|
|
return pkg.Name()
|
|
}
|
|
var actual []string
|
|
for _, name := range checked.Scope().Names() {
|
|
if !token.IsExported(name) {
|
|
continue
|
|
}
|
|
object := checked.Scope().Lookup(name)
|
|
switch object := object.(type) {
|
|
case *types.Const:
|
|
actual = append(actual, fmt.Sprintf("const %s = %s", object.Name(), object.Val().ExactString()))
|
|
case *types.TypeName:
|
|
named, ok := object.Type().(*types.Named)
|
|
if !ok {
|
|
actual = append(actual, types.ObjectString(object, qualifier))
|
|
break
|
|
}
|
|
if structure, ok := named.Underlying().(*types.Struct); ok {
|
|
var fields []string
|
|
for index := 0; index < structure.NumFields(); index++ {
|
|
field := structure.Field(index)
|
|
if field.Exported() {
|
|
fields = append(fields, field.Name()+" "+types.TypeString(field.Type(), qualifier))
|
|
}
|
|
}
|
|
if len(fields) == 0 {
|
|
actual = append(actual, "type "+object.Name()+" struct{ /* opaque */ }")
|
|
} else {
|
|
actual = append(actual, "type "+object.Name()+" struct{"+strings.Join(fields, "; ")+"}")
|
|
}
|
|
} else {
|
|
actual = append(actual, types.ObjectString(object, qualifier))
|
|
}
|
|
default:
|
|
actual = append(actual, types.ObjectString(object, qualifier))
|
|
}
|
|
typeName, ok := object.(*types.TypeName)
|
|
if !ok {
|
|
continue
|
|
}
|
|
named, ok := typeName.Type().(*types.Named)
|
|
if !ok {
|
|
continue
|
|
}
|
|
for index := 0; index < named.NumMethods(); index++ {
|
|
method := named.Method(index)
|
|
if method.Exported() {
|
|
actual = append(actual, types.ObjectString(method, qualifier))
|
|
}
|
|
}
|
|
}
|
|
sort.Strings(actual)
|
|
got := strings.Join(actual, "\n") + "\n"
|
|
want, err := os.ReadFile("testdata/public-api-v1.txt")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want = []byte(strings.TrimPrefix(string(want), "# SPDX-License-Identifier: Apache-2.0\n\n"))
|
|
if string(want) != got {
|
|
t.Fatalf("v1 public API drifted\n--- committed contract ---\n%s--- observed API ---\n%s", want, got)
|
|
}
|
|
}
|