delve/service/dap/daptest/gen/main.go
Hyang-Ah Hana Kim 4b4f7a589a
daptest/gen: autogenerate some assertion functions (#2441)
daptest has type assertion functions that panic
if the read response/event message is not
the expected type. This is not only against the
recommended style guideline (Don't Panic, Useful
Test Failures, ...), but also it prevents from
quickly diagnosing test failures occurred in remote
CIs.

This PR changes the type assertion to the two
return value type assertion, and t.Fatal with details
if the type is not expected.
service/dap/daptest/main.go is a program that auto
generates those assertion functions in resp.go.

Run `go generate` from the service/dap directory
to update resp.go.
2021-05-06 11:11:45 +02:00

100 lines
2.2 KiB
Go

// Binary gen generates service/dap/daptest/responses.go.
package main
import (
"bytes"
"flag"
"fmt"
"go/format"
"go/types"
"io/ioutil"
"os"
"text/template"
"golang.org/x/tools/go/packages"
_ "github.com/google/go-dap"
)
var oFlag = flag.String("o", "", "output file name")
var tmpl = template.Must(template.New("assert").Parse(`package daptest
// Code generated by go generate; DO NOT EDIT.
// The code generator program is in ./gen directory.
import (
"testing"
"github.com/google/go-dap"
)
{{range .}}
// Expect{{.}} reads a protocol message from the connection
// and fails the test if the read message is not *{{.}}.
func (c *Client) Expect{{.}}(t *testing.T) *dap.{{.}} {
t.Helper()
m := c.ExpectMessage(t)
r, ok := m.(*dap.{{.}})
if !ok {
t.Fatalf("got %q, want *dap.{{.}}", m)
}
return r
}{{end}}
`))
func main() {
flag.Parse()
if *oFlag == "" {
fmt.Fprintf(os.Stderr, "-o must be provided\n")
}
pkgs, err := packages.Load(&packages.Config{
Mode: packages.NeedTypes,
}, "github.com/google/go-dap")
if err != nil {
fmt.Fprintf(os.Stderr, "load: %v\n", err)
os.Exit(1)
}
if len(pkgs) != 1 || pkgs[0].Types == nil {
fmt.Fprintf(os.Stderr, "invalid package was loaded: %#v\n", pkgs)
os.Exit(1)
}
messages := []string{}
scope := pkgs[0].Types.Scope()
for _, name := range scope.Names() {
// Find only types that are embedding go-dap.Respose message.
obj := scope.Lookup(name)
if !obj.Exported() {
continue // skip unexported
}
u, ok := obj.Type().Underlying().(*types.Struct)
if !ok {
continue
}
for i := 0; i < u.NumFields(); i++ {
if f := u.Field(i); f.Embedded() && (f.Type().String() == "github.com/google/go-dap.Response" ||
f.Type().String() == "github.com/google/go-dap.Event") {
messages = append(messages, obj.Name())
break
}
}
}
buf := &bytes.Buffer{}
if err := tmpl.Execute(buf, messages); err != nil {
fmt.Fprintf(os.Stderr, "Failed to generate: %v\n", err)
os.Exit(1)
}
formatted, err := format.Source(buf.Bytes())
if err != nil {
fmt.Fprintf(os.Stderr, "Generated invalid go code: %v\n", err)
os.Exit(1)
}
if err := ioutil.WriteFile(*oFlag, formatted, 0644); err != nil {
fmt.Fprintf(os.Stderr, "Failed to write: %v\n", err)
os.Exit(1)
}
}