/cgo/cgo_test.go
https://github.com/tinygo-org/tinygo · Go · 145 lines · 104 code · 15 blank · 26 comment · 25 complexity · bc7534c2c0f71805ad7e4c291ddbaca8 MD5 · raw file
- package cgo
- import (
- "bytes"
- "flag"
- "fmt"
- "go/ast"
- "go/format"
- "go/parser"
- "go/token"
- "go/types"
- "io/ioutil"
- "path/filepath"
- "regexp"
- "runtime"
- "strings"
- "testing"
- )
- // Pass -update to go test to update the output of the test files.
- var flagUpdate = flag.Bool("update", false, "Update images based on test output.")
- // normalizeResult normalizes Go source code that comes out of tests across
- // platforms and Go versions.
- func normalizeResult(result string) string {
- actual := strings.Replace(result, "\r\n", "\n", -1)
- // Make sure all functions are wrapped, even those that would otherwise be
- // single-line functions. This is necessary because Go 1.14 changed the way
- // such functions are wrapped and it's important to have consistent test
- // results.
- re := regexp.MustCompile(`func \((.+)\)( .*?) +{ (.+) }`)
- actual = re.ReplaceAllString(actual, "func ($1)$2 {\n\t$3\n}")
- return actual
- }
- func TestCGo(t *testing.T) {
- var cflags = []string{"--target=armv6m-none-eabi"}
- for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
- name := name // avoid a race condition
- t.Run(name, func(t *testing.T) {
- // Read the AST in memory.
- path := filepath.Join("testdata", name+".go")
- fset := token.NewFileSet()
- f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
- if err != nil {
- t.Fatal("could not parse Go source file:", err)
- }
- // Process the AST with CGo.
- cgoAST, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
- // Check the AST for type errors.
- var typecheckErrors []error
- config := types.Config{
- Error: func(err error) {
- typecheckErrors = append(typecheckErrors, err)
- },
- Importer: simpleImporter{},
- Sizes: types.SizesFor("gccgo", "arm"),
- }
- _, err = config.Check("", fset, []*ast.File{f, cgoAST}, nil)
- if err != nil && len(typecheckErrors) == 0 {
- // Only report errors when no type errors are found (an
- // unexpected condition).
- t.Error(err)
- }
- // Store the (formatted) output in a buffer. Format it, so it
- // becomes easier to read (and will hopefully change less with CGo
- // changes).
- buf := &bytes.Buffer{}
- if len(cgoErrors) != 0 {
- buf.WriteString("// CGo errors:\n")
- for _, err := range cgoErrors {
- buf.WriteString(formatDiagnostic(err))
- }
- buf.WriteString("\n")
- }
- if len(typecheckErrors) != 0 {
- buf.WriteString("// Type checking errors after CGo processing:\n")
- for _, err := range typecheckErrors {
- buf.WriteString(formatDiagnostic(err))
- }
- buf.WriteString("\n")
- }
- err = format.Node(buf, fset, cgoAST)
- if err != nil {
- t.Errorf("could not write out CGo AST: %v", err)
- }
- actual := normalizeResult(string(buf.Bytes()))
- // Read the file with the expected output, to compare against.
- outfile := filepath.Join("testdata", name+".out.go")
- expectedBytes, err := ioutil.ReadFile(outfile)
- if err != nil {
- t.Fatalf("could not read expected output: %v", err)
- }
- expected := strings.Replace(string(expectedBytes), "\r\n", "\n", -1)
- // Check whether the output is as expected.
- if expected != actual {
- // It is not. Test failed.
- if *flagUpdate {
- // Update the file with the expected data.
- err := ioutil.WriteFile(outfile, []byte(actual), 0666)
- if err != nil {
- t.Error("could not write updated output file:", err)
- }
- return
- }
- t.Errorf("output did not match:\n%s", string(actual))
- }
- })
- }
- }
- // simpleImporter implements the types.Importer interface, but only allows
- // importing the unsafe package.
- type simpleImporter struct {
- }
- // Import implements the Importer interface. For testing usage only: it only
- // supports importing the unsafe package.
- func (i simpleImporter) Import(path string) (*types.Package, error) {
- switch path {
- case "unsafe":
- return types.Unsafe, nil
- default:
- return nil, fmt.Errorf("importer not implemented for package %s", path)
- }
- }
- // formatDiagnostics formats the error message to be an indented comment. It
- // also fixes Windows path name issues (backward slashes).
- func formatDiagnostic(err error) string {
- msg := err.Error()
- if runtime.GOOS == "windows" {
- // Fix Windows path slashes.
- msg = strings.Replace(msg, "testdata\\", "testdata/", -1)
- }
- return "// " + msg + "\n"
- }