gobind_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // Copyright 2016 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "bytes"
  7. "fmt"
  8. "os"
  9. "os/exec"
  10. "runtime"
  11. "strings"
  12. "testing"
  13. "golang.org/x/tools/go/packages/packagestest"
  14. )
  15. func TestMain(m *testing.M) {
  16. // To avoid recompiling the gobind command (and to support compiler options
  17. // like -race and -coverage), allow the test binary itself to re-exec itself
  18. // as the gobind command by setting an environment variable.
  19. if os.Getenv("GOBIND_TEST_IS_GOBIND") != "" {
  20. main()
  21. os.Exit(0)
  22. }
  23. os.Setenv("GOBIND_TEST_IS_GOBIND", "1")
  24. os.Exit(m.Run())
  25. }
  26. var tests = []struct {
  27. name string
  28. lang string
  29. pkg string
  30. goos string
  31. // reverse is true if the test needs to generate reverse bindings using
  32. // external tools such as javap.
  33. reverse bool
  34. }{
  35. {
  36. name: "ObjC-Testpkg",
  37. lang: "objc",
  38. pkg: "golang.org/x/mobile/bind/testdata/testpkg",
  39. },
  40. {
  41. name: "Java-Testpkg",
  42. lang: "java",
  43. pkg: "golang.org/x/mobile/bind/testdata/testpkg",
  44. },
  45. {
  46. name: "Go-Testpkg",
  47. lang: "go",
  48. pkg: "golang.org/x/mobile/bind/testdata/testpkg",
  49. },
  50. {
  51. name: "Java-Javapkg",
  52. lang: "java",
  53. pkg: "golang.org/x/mobile/bind/testdata/testpkg/javapkg",
  54. goos: "android",
  55. reverse: true,
  56. },
  57. {
  58. name: "Go-Javapkg",
  59. lang: "go",
  60. pkg: "golang.org/x/mobile/bind/testdata/testpkg/javapkg",
  61. goos: "android",
  62. reverse: true,
  63. },
  64. {
  65. name: "Go-Cgopkg",
  66. lang: "go,java,objc",
  67. pkg: "golang.org/x/mobile/bind/testdata/cgopkg",
  68. goos: "android",
  69. },
  70. }
  71. func mustHaveBindTestdata(t testing.TB) {
  72. switch runtime.GOOS {
  73. case "android", "ios":
  74. t.Skipf("skipping: test cannot access ../../bind/testdata on %s/%s", runtime.GOOS, runtime.GOARCH)
  75. }
  76. }
  77. func gobindBin(t testing.TB) string {
  78. switch runtime.GOOS {
  79. case "js", "ios":
  80. t.Skipf("skipping: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH)
  81. }
  82. p, err := os.Executable()
  83. if err != nil {
  84. t.Fatal(err)
  85. }
  86. return p
  87. }
  88. func runGobind(t testing.TB, lang, pkg, goos string, exported *packagestest.Exported) error {
  89. cmd := exec.Command(gobindBin(t), "-lang", lang, pkg)
  90. cmd.Dir = exported.Config.Dir
  91. cmd.Env = exported.Config.Env
  92. if goos != "" {
  93. // Add CGO_ENABLED=1 explicitly since Cgo is disabled when GOOS is different from host OS.
  94. cmd.Env = append(cmd.Env, "GOOS="+goos, "CGO_ENABLED=1")
  95. }
  96. stderr := new(strings.Builder)
  97. cmd.Stderr = stderr
  98. stdout := new(strings.Builder)
  99. cmd.Stdout = stdout
  100. err := cmd.Run()
  101. if testing.Verbose() && stdout.Len() > 0 {
  102. t.Logf("stdout (%v):\n%s", cmd, stderr)
  103. }
  104. if stderr.Len() > 0 {
  105. t.Logf("stderr (%v):\n%s", cmd, stderr)
  106. }
  107. if err != nil {
  108. return fmt.Errorf("%v: %w", cmd, err)
  109. }
  110. return nil
  111. }
  112. func TestGobind(t *testing.T) { packagestest.TestAll(t, testGobind) }
  113. func testGobind(t *testing.T, exporter packagestest.Exporter) {
  114. mustHaveBindTestdata(t)
  115. _, javapErr := exec.LookPath("javap")
  116. exported := packagestest.Export(t, exporter, []packagestest.Module{{
  117. Name: "golang.org/x/mobile",
  118. Files: packagestest.MustCopyFileTree("../.."),
  119. }})
  120. defer exported.Cleanup()
  121. for _, test := range tests {
  122. t.Run(test.name, func(t *testing.T) {
  123. if exporter == packagestest.Modules && test.reverse {
  124. t.Skip("reverse binding does't work with Go modules")
  125. }
  126. if test.reverse && javapErr != nil {
  127. t.Skip("reverse bind test requires javap which is not available")
  128. }
  129. if err := runGobind(t, test.lang, test.pkg, test.goos, exported); err != nil {
  130. t.Error(err)
  131. }
  132. })
  133. }
  134. }
  135. func TestDocs(t *testing.T) { packagestest.TestAll(t, testDocs) }
  136. func testDocs(t *testing.T, exporter packagestest.Exporter) {
  137. mustHaveBindTestdata(t)
  138. const docsrc = `
  139. package doctest
  140. // This is a comment.
  141. type Struct struct{
  142. }`
  143. exported := packagestest.Export(t, exporter, []packagestest.Module{
  144. {
  145. Name: "example.com/doctest",
  146. Files: map[string]interface{}{
  147. "doc.go": docsrc,
  148. },
  149. },
  150. {
  151. // gobind requires golang.org/x/mobile to generate code for reverse bindings.
  152. Name: "golang.org/x/mobile",
  153. Files: packagestest.MustCopyFileTree("../.."),
  154. },
  155. })
  156. defer exported.Cleanup()
  157. const comment = "This is a comment."
  158. for _, lang := range []string{"java", "objc"} {
  159. cmd := exec.Command(gobindBin(t), "-lang", lang, "example.com/doctest")
  160. cmd.Dir = exported.Config.Dir
  161. cmd.Env = exported.Config.Env
  162. out, err := cmd.CombinedOutput()
  163. if err != nil {
  164. t.Errorf("gobind -lang %s failed: %v: %s", lang, err, out)
  165. continue
  166. }
  167. if bytes.Index(out, []byte(comment)) == -1 {
  168. t.Errorf("gobind output for language %s did not contain the comment %q", lang, comment)
  169. }
  170. }
  171. }
  172. func BenchmarkGobind(b *testing.B) {
  173. packagestest.BenchmarkAll(b, benchmarkGobind)
  174. }
  175. func benchmarkGobind(b *testing.B, exporter packagestest.Exporter) {
  176. _, javapErr := exec.LookPath("javap")
  177. exported := packagestest.Export(b, exporter, []packagestest.Module{{
  178. Name: "golang.org/x/mobile",
  179. Files: packagestest.MustCopyFileTree("../.."),
  180. }})
  181. defer exported.Cleanup()
  182. for _, test := range tests {
  183. b.Run(test.name, func(b *testing.B) {
  184. if exporter == packagestest.Modules && test.reverse {
  185. b.Skip("reverse binding does't work with Go modules")
  186. }
  187. if test.reverse && javapErr != nil {
  188. b.Skip("reverse bind test requires javap which is not available")
  189. }
  190. for i := 0; i < b.N; i++ {
  191. if err := runGobind(b, test.lang, test.pkg, test.goos, exported); err != nil {
  192. b.Error(err)
  193. }
  194. }
  195. })
  196. }
  197. }