aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2023-05-04 12:13:27 -0400
committerAustin Clements <austin@google.com>2023-05-12 12:00:07 +0000
commit0efbec91cdc8b3e73a869e13dcec73c798147bd6 (patch)
treea1f898783deccb17557f5ab21551f2b1d506c818 /src/cmd
parent2484e1331a6054ffa25e2e6a4146943251171c56 (diff)
downloadgo-0efbec91cdc8b3e73a869e13dcec73c798147bd6.tar.gz
go-0efbec91cdc8b3e73a869e13dcec73c798147bd6.zip
misc/swig: move tests to cmd/cgo/internal
This moves the misc/swig test to cmd/cgo/internal. This lets these tests access facilities in internal/testenv. It's also now just a normal test that can run as part of the cmd tests. For #37486. Change-Id: Ibe5026219999d175aa0a310b9886bef3f6f9ed17 Reviewed-on: https://go-review.googlesource.com/c/go/+/492722 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Bryan Mills <bcmills@google.com>
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/cgo/internal/swig/swig_test.go153
-rw-r--r--src/cmd/cgo/internal/swig/testdata/callback/main.cc15
-rw-r--r--src/cmd/cgo/internal/swig/testdata/callback/main.go60
-rw-r--r--src/cmd/cgo/internal/swig/testdata/callback/main.h20
-rw-r--r--src/cmd/cgo/internal/swig/testdata/callback/main.swigcxx18
-rw-r--r--src/cmd/cgo/internal/swig/testdata/stdio/main.go45
-rw-r--r--src/cmd/cgo/internal/swig/testdata/stdio/main.swig24
-rw-r--r--src/cmd/dist/test.go3
8 files changed, 335 insertions, 3 deletions
diff --git a/src/cmd/cgo/internal/swig/swig_test.go b/src/cmd/cgo/internal/swig/swig_test.go
new file mode 100644
index 0000000000..41563138a7
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/swig_test.go
@@ -0,0 +1,153 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package swig
+
+import (
+ "cmd/internal/quoted"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "sync"
+ "testing"
+)
+
+func TestStdio(t *testing.T) {
+ testenv.MustHaveCGO(t)
+ mustHaveSwig(t)
+ run(t, "testdata/stdio", false)
+}
+
+func TestCall(t *testing.T) {
+ testenv.MustHaveCGO(t)
+ mustHaveSwig(t)
+ mustHaveCxx(t)
+ run(t, "testdata/callback", false, "Call")
+ t.Run("lto", func(t *testing.T) { run(t, "testdata/callback", true, "Call") })
+}
+
+func TestCallback(t *testing.T) {
+ testenv.MustHaveCGO(t)
+ mustHaveSwig(t)
+ mustHaveCxx(t)
+ run(t, "testdata/callback", false, "Callback")
+ t.Run("lto", func(t *testing.T) { run(t, "testdata/callback", true, "Callback") })
+}
+
+func run(t *testing.T, dir string, lto bool, args ...string) {
+ runArgs := append([]string{"run", "."}, args...)
+ cmd := exec.Command("go", runArgs...)
+ cmd.Dir = dir
+ if lto {
+ const cflags = "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option"
+ cmd.Env = append(cmd.Environ(),
+ "CGO_CFLAGS="+cflags,
+ "CGO_CXXFLAGS="+cflags,
+ "CGO_LDFLAGS="+cflags)
+ }
+ out, err := cmd.CombinedOutput()
+ if string(out) != "OK\n" {
+ t.Errorf("%s", string(out))
+ }
+ if err != nil {
+ t.Errorf("%s", err)
+ }
+}
+
+func mustHaveCxx(t *testing.T) {
+ // Ask the go tool for the CXX it's configured to use.
+ cxx, err := exec.Command("go", "env", "CXX").CombinedOutput()
+ if err != nil {
+ t.Fatalf("go env CXX failed: %s", err)
+ }
+ args, err := quoted.Split(string(cxx))
+ if err != nil {
+ t.Skipf("could not parse 'go env CXX' output %q: %s", string(cxx), err)
+ }
+ if len(args) == 0 {
+ t.Skip("no C++ compiler")
+ }
+ testenv.MustHaveExecPath(t, string(args[0]))
+}
+
+var (
+ swigOnce sync.Once
+ haveSwig bool
+)
+
+func mustHaveSwig(t *testing.T) {
+ swigOnce.Do(func() {
+ mustHaveSwigOnce(t)
+ haveSwig = true
+ })
+ // The first call will skip t with a nice message. On later calls, we just skip.
+ if !haveSwig {
+ t.Skip("swig not found")
+ }
+}
+
+func mustHaveSwigOnce(t *testing.T) {
+ swig, err := exec.LookPath("swig")
+ if err != nil {
+ t.Skipf("swig not in PATH: %s", err)
+ }
+
+ // Check that swig was installed with Go support by checking
+ // that a go directory exists inside the swiglib directory.
+ // See https://golang.org/issue/23469.
+ output, err := exec.Command(swig, "-go", "-swiglib").Output()
+ if err != nil {
+ t.Skip("swig is missing Go support")
+ }
+ swigDir := strings.TrimSpace(string(output))
+
+ _, err = os.Stat(filepath.Join(swigDir, "go"))
+ if err != nil {
+ t.Skip("swig is missing Go support")
+ }
+
+ // Check that swig has a new enough version.
+ // See https://golang.org/issue/22858.
+ out, err := exec.Command(swig, "-version").CombinedOutput()
+ if err != nil {
+ t.Skipf("failed to get swig version:%s\n%s", err, string(out))
+ }
+
+ re := regexp.MustCompile(`[vV]ersion +(\d+)([.]\d+)?([.]\d+)?`)
+ matches := re.FindSubmatch(out)
+ if matches == nil {
+ // Can't find version number; hope for the best.
+ t.Logf("failed to find swig version, continuing")
+ return
+ }
+
+ var parseError error
+ atoi := func(s string) int {
+ x, err := strconv.Atoi(s)
+ if err != nil && parseError == nil {
+ parseError = err
+ }
+ return x
+ }
+ var major, minor, patch int
+ major = atoi(string(matches[1]))
+ if len(matches[2]) > 0 {
+ minor = atoi(string(matches[2][1:]))
+ }
+ if len(matches[3]) > 0 {
+ patch = atoi(string(matches[3][1:]))
+ }
+ if parseError != nil {
+ t.Logf("error parsing swig version %q, continuing anyway: %s", string(matches[0]), parseError)
+ return
+ }
+ t.Logf("found swig version %d.%d.%d", major, minor, patch)
+ if major < 3 || (major == 3 && minor == 0 && patch < 6) {
+ t.Skip("test requires swig 3.0.6 or later")
+ }
+}
diff --git a/src/cmd/cgo/internal/swig/testdata/callback/main.cc b/src/cmd/cgo/internal/swig/testdata/callback/main.cc
new file mode 100644
index 0000000000..7de917cde4
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/callback/main.cc
@@ -0,0 +1,15 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This .cc file will be automatically compiled by the go tool and
+// included in the package.
+
+#include <string>
+#include "main.h"
+
+std::string Caller::call() {
+ if (callback_ != 0)
+ return callback_->run();
+ return "";
+}
diff --git a/src/cmd/cgo/internal/swig/testdata/callback/main.go b/src/cmd/cgo/internal/swig/testdata/callback/main.go
new file mode 100644
index 0000000000..73034a0c7c
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/callback/main.go
@@ -0,0 +1,60 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+)
+
+func main() {
+ if len(os.Args) != 2 {
+ fatal("usage: callback testname")
+ }
+ switch os.Args[1] {
+ default:
+ fatal("unknown test %q", os.Args[1])
+ case "Call":
+ testCall()
+ case "Callback":
+ testCallback()
+ }
+ println("OK")
+}
+
+func fatal(f string, args ...any) {
+ fmt.Fprintln(os.Stderr, fmt.Sprintf(f, args...))
+ os.Exit(1)
+}
+
+type GoCallback struct{}
+
+func (p *GoCallback) Run() string {
+ return "GoCallback.Run"
+}
+
+func testCall() {
+ c := NewCaller()
+ cb := NewCallback()
+
+ c.SetCallback(cb)
+ s := c.Call()
+ if s != "Callback::run" {
+ fatal("unexpected string from Call: %q", s)
+ }
+ c.DelCallback()
+}
+
+func testCallback() {
+ c := NewCaller()
+ cb := NewDirectorCallback(&GoCallback{})
+ c.SetCallback(cb)
+ s := c.Call()
+ if s != "GoCallback.Run" {
+ fatal("unexpected string from Call with callback: %q", s)
+ }
+ c.DelCallback()
+ DeleteDirectorCallback(cb)
+}
diff --git a/src/cmd/cgo/internal/swig/testdata/callback/main.h b/src/cmd/cgo/internal/swig/testdata/callback/main.h
new file mode 100644
index 0000000000..4b661060d8
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/callback/main.h
@@ -0,0 +1,20 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+class Callback {
+public:
+ virtual ~Callback() { }
+ virtual std::string run() { return "Callback::run"; }
+};
+
+class Caller {
+private:
+ Callback *callback_;
+public:
+ Caller(): callback_(0) { }
+ ~Caller() { delCallback(); }
+ void delCallback() { delete callback_; callback_ = 0; }
+ void setCallback(Callback *cb) { delCallback(); callback_ = cb; }
+ std::string call();
+};
diff --git a/src/cmd/cgo/internal/swig/testdata/callback/main.swigcxx b/src/cmd/cgo/internal/swig/testdata/callback/main.swigcxx
new file mode 100644
index 0000000000..0fd73d6362
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/callback/main.swigcxx
@@ -0,0 +1,18 @@
+/* Copyright 2011 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file. */
+
+/* An example of writing a C++ virtual function in Go. */
+
+%module(directors="1") callback
+
+%{
+#include <string>
+#include "main.h"
+%}
+
+%include "std_string.i"
+
+%feature("director");
+
+%include "main.h"
diff --git a/src/cmd/cgo/internal/swig/testdata/stdio/main.go b/src/cmd/cgo/internal/swig/testdata/stdio/main.go
new file mode 100644
index 0000000000..0296dd3224
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/stdio/main.go
@@ -0,0 +1,45 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is here just to cause problems.
+// main.swig turns into a file also named main.go.
+// Make sure cmd/go keeps them separate
+// when both are passed to cgo.
+
+package main
+
+//int F(void) { return 1; }
+import "C"
+import (
+ "fmt"
+ "os"
+)
+
+func F() int { return int(C.F()) }
+
+func main() {
+ if x := int(C.F()); x != 1 {
+ fatal("x = %d, want 1", x)
+ }
+
+ // Open this file itself and verify that the first few characters are
+ // as expected.
+ f := Fopen("main.go", "r")
+ if f.Swigcptr() == 0 {
+ fatal("fopen failed")
+ }
+ if Fgetc(f) != '/' || Fgetc(f) != '/' || Fgetc(f) != ' ' || Fgetc(f) != 'C' {
+ fatal("read unexpected characters")
+ }
+ if Fclose(f) != 0 {
+ fatal("fclose failed")
+ }
+
+ println("OK")
+}
+
+func fatal(f string, args ...any) {
+ fmt.Fprintln(os.Stderr, fmt.Sprintf(f, args...))
+ os.Exit(1)
+}
diff --git a/src/cmd/cgo/internal/swig/testdata/stdio/main.swig b/src/cmd/cgo/internal/swig/testdata/stdio/main.swig
new file mode 100644
index 0000000000..b28ae0a6b7
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/stdio/main.swig
@@ -0,0 +1,24 @@
+/* Copyright 2011 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file. */
+
+/* A trivial example of wrapping a C library using SWIG. */
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+%}
+
+%typemap(gotype) const char * "string"
+%typemap(in) const char * %{
+ $1 = malloc($input.n + 1);
+ memcpy($1, $input.p, $input.n);
+ $1[$input.n] = '\0';
+%}
+%typemap(freearg) const char * %{
+ free($1);
+%}
+
+FILE *fopen(const char *name, const char *mode);
+int fclose(FILE *);
+int fgetc(FILE *);
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 95c27ce327..5a47b86bc6 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -870,9 +870,6 @@ func (t *tester) registerTests() {
if goos != "android" {
t.registerTest("cgo_testfortran", "", &goTest{dir: "cmd/cgo/internal/testfortran", timeout: 5 * time.Minute}, rtHostTest{})
}
- if goos != "android" {
- t.registerTest("swig", "", &goTest{dir: "../misc/swig"})
- }
}
if t.cgoEnabled {
t.registerCgoTests()