github.com/F4RD1N/gomobile@v1.0.1/cmd/gomobile/bind_test.go (about)

     1  // Copyright 2015 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  
     5  package main
     6  
     7  import (
     8  	"bytes"
     9  	"io/ioutil"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"runtime"
    14  	"strings"
    15  	"testing"
    16  	"text/template"
    17  )
    18  
    19  func TestBindAndroid(t *testing.T) {
    20  	androidHome := os.Getenv("ANDROID_HOME")
    21  	if androidHome == "" {
    22  		t.Skip("ANDROID_HOME not found, skipping bind")
    23  	}
    24  	platform, err := androidAPIPath()
    25  	if err != nil {
    26  		t.Skip("No android API platform found in $ANDROID_HOME, skipping bind")
    27  	}
    28  	platform = strings.Replace(platform, androidHome, "$ANDROID_HOME", -1)
    29  
    30  	defer func() {
    31  		xout = os.Stderr
    32  		buildN = false
    33  		buildX = false
    34  		buildO = ""
    35  		buildTarget = ""
    36  		bindJavaPkg = ""
    37  	}()
    38  	buildN = true
    39  	buildX = true
    40  	buildO = "asset.aar"
    41  	buildTarget = "android/arm"
    42  
    43  	tests := []struct {
    44  		javaPkg string
    45  	}{
    46  		{
    47  			// Empty javaPkg
    48  		},
    49  		{
    50  			javaPkg: "com.example.foo",
    51  		},
    52  	}
    53  	for _, tc := range tests {
    54  		bindJavaPkg = tc.javaPkg
    55  
    56  		buf := new(bytes.Buffer)
    57  		xout = buf
    58  		gopath = filepath.SplitList(goEnv("GOPATH"))[0]
    59  		if goos == "windows" {
    60  			os.Setenv("HOMEDRIVE", "C:")
    61  		}
    62  		cmdBind.flag.Parse([]string{"github.com/F4RD1N/gomobile/asset"})
    63  		err := runBind(cmdBind)
    64  		if err != nil {
    65  			t.Log(buf.String())
    66  			t.Fatal(err)
    67  		}
    68  		got := filepath.ToSlash(buf.String())
    69  
    70  		output, err := defaultOutputData()
    71  		if err != nil {
    72  			t.Fatal(err)
    73  		}
    74  		data := struct {
    75  			outputData
    76  			AndroidPlatform string
    77  			JavaPkg         string
    78  		}{
    79  			outputData:      output,
    80  			AndroidPlatform: platform,
    81  			JavaPkg:         tc.javaPkg,
    82  		}
    83  
    84  		wantBuf := new(bytes.Buffer)
    85  		if err := bindAndroidTmpl.Execute(wantBuf, data); err != nil {
    86  			t.Errorf("%+v: computing diff failed: %v", tc, err)
    87  			continue
    88  		}
    89  
    90  		diff, err := diff(got, wantBuf.String())
    91  		if err != nil {
    92  			t.Errorf("%+v: computing diff failed: %v", tc, err)
    93  			continue
    94  		}
    95  		if diff != "" {
    96  			t.Errorf("%+v: unexpected output:\n%s", tc, diff)
    97  		}
    98  	}
    99  }
   100  
   101  func TestBindIOS(t *testing.T) {
   102  	if !xcodeAvailable() {
   103  		t.Skip("Xcode is missing")
   104  	}
   105  	defer func() {
   106  		xout = os.Stderr
   107  		buildN = false
   108  		buildX = false
   109  		buildO = ""
   110  		buildTarget = ""
   111  		bindPrefix = ""
   112  	}()
   113  	buildN = true
   114  	buildX = true
   115  	buildO = "Asset.framework"
   116  	buildTarget = "ios/arm64"
   117  
   118  	tests := []struct {
   119  		prefix string
   120  		out    string
   121  	}{
   122  		{
   123  			// empty prefix
   124  		},
   125  		{
   126  			prefix: "Foo",
   127  		},
   128  		{
   129  			out: "Abcde.framework",
   130  		},
   131  	}
   132  	for _, tc := range tests {
   133  		bindPrefix = tc.prefix
   134  		if tc.out != "" {
   135  			buildO = tc.out
   136  		}
   137  
   138  		buf := new(bytes.Buffer)
   139  		xout = buf
   140  		gopath = filepath.SplitList(goEnv("GOPATH"))[0]
   141  		if goos == "windows" {
   142  			os.Setenv("HOMEDRIVE", "C:")
   143  		}
   144  		cmdBind.flag.Parse([]string{"github.com/F4RD1N/gomobile/asset"})
   145  		if err := runBind(cmdBind); err != nil {
   146  			t.Log(buf.String())
   147  			t.Fatal(err)
   148  		}
   149  		got := filepath.ToSlash(buf.String())
   150  
   151  		output, err := defaultOutputData()
   152  		if err != nil {
   153  			t.Fatal(err)
   154  		}
   155  
   156  		data := struct {
   157  			outputData
   158  			Output         string
   159  			Prefix         string
   160  			BitcodeEnabled bool
   161  		}{
   162  			outputData:     output,
   163  			Output:         buildO[:len(buildO)-len(".framework")],
   164  			Prefix:         tc.prefix,
   165  			BitcodeEnabled: bitcodeEnabled,
   166  		}
   167  
   168  		wantBuf := new(bytes.Buffer)
   169  		if err := bindIOSTmpl.Execute(wantBuf, data); err != nil {
   170  			t.Errorf("%+v: computing diff failed: %v", tc, err)
   171  			continue
   172  		}
   173  
   174  		diff, err := diff(got, wantBuf.String())
   175  		if err != nil {
   176  			t.Errorf("%+v: computing diff failed: %v", tc, err)
   177  			continue
   178  		}
   179  		if diff != "" {
   180  			t.Errorf("%+v: unexpected output:\n%s", tc, diff)
   181  		}
   182  	}
   183  }
   184  
   185  var bindAndroidTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile
   186  WORK=$WORK
   187  GOOS=android CGO_ENABLED=1 gobind -lang=go,java -outdir=$WORK{{if .JavaPkg}} -javapkg={{.JavaPkg}}{{end}} github.com/F4RD1N/gomobile/asset
   188  mkdir -p $WORK/src
   189  PWD=$WORK/src GOOS=android GOARCH=arm CC=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang CXX=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang++ CGO_ENABLED=1 GOARM=7 GOPATH=$WORK:$GOPATH go mod tidy
   190  PWD=$WORK/src GOOS=android GOARCH=arm CC=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang CXX=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang++ CGO_ENABLED=1 GOARM=7 GOPATH=$WORK:$GOPATH go build -x -buildmode=c-shared -o=$WORK/android/src/main/jniLibs/armeabi-v7a/libgojni.so ./gobind
   191  PWD=$WORK/java javac -d $WORK/javac-output -source 1.7 -target 1.7 -bootclasspath {{.AndroidPlatform}}/android.jar *.java
   192  jar c -C $WORK/javac-output .
   193  `))
   194  
   195  var bindIOSTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile
   196  WORK=$WORK
   197  GOOS=darwin CGO_ENABLED=1 gobind -lang=go,objc -outdir=$WORK -tags=ios{{if .Prefix}} -prefix={{.Prefix}}{{end}} github.com/F4RD1N/gomobile/asset
   198  mkdir -p $WORK/src
   199  PWD=$WORK/src GOOS=darwin GOARCH=arm64 CC=iphoneos-clang CXX=iphoneos-clang++ CGO_CFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 {{if .BitcodeEnabled}}-fembed-bitcode {{end}}-arch arm64 CGO_CXXFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 {{if .BitcodeEnabled}}-fembed-bitcode {{end}}-arch arm64 CGO_LDFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 {{if .BitcodeEnabled}}-fembed-bitcode {{end}}-arch arm64 CGO_ENABLED=1 GOPATH=$WORK:$GOPATH go build -tags ios -x -buildmode=c-archive -o $WORK/{{.Output}}-arm64.a ./gobind
   200  rm -r -f "{{.Output}}.framework"
   201  mkdir -p {{.Output}}.framework/Versions/A/Headers
   202  ln -s A {{.Output}}.framework/Versions/Current
   203  ln -s Versions/Current/Headers {{.Output}}.framework/Headers
   204  ln -s Versions/Current/{{.Output}} {{.Output}}.framework/{{.Output}}
   205  xcrun lipo -create -arch arm64 $WORK/{{.Output}}-arm64.a -o {{.Output}}.framework/Versions/A/{{.Output}}
   206  cp $WORK/src/gobind/{{.Prefix}}Asset.objc.h {{.Output}}.framework/Versions/A/Headers/{{.Prefix}}Asset.objc.h
   207  mkdir -p {{.Output}}.framework/Versions/A/Headers
   208  cp $WORK/src/gobind/Universe.objc.h {{.Output}}.framework/Versions/A/Headers/Universe.objc.h
   209  mkdir -p {{.Output}}.framework/Versions/A/Headers
   210  cp $WORK/src/gobind/ref.h {{.Output}}.framework/Versions/A/Headers/ref.h
   211  mkdir -p {{.Output}}.framework/Versions/A/Headers
   212  mkdir -p {{.Output}}.framework/Versions/A/Headers
   213  mkdir -p {{.Output}}.framework/Versions/A/Resources
   214  ln -s Versions/Current/Resources {{.Output}}.framework/Resources
   215  mkdir -p {{.Output}}.framework/Resources
   216  mkdir -p {{.Output}}.framework/Versions/A/Modules
   217  ln -s Versions/Current/Modules {{.Output}}.framework/Modules
   218  `))
   219  
   220  func TestBindIOSAll(t *testing.T) {
   221  	if !xcodeAvailable() {
   222  		t.Skip("Xcode is missing")
   223  	}
   224  	defer func() {
   225  		xout = os.Stderr
   226  		buildN = false
   227  		buildX = false
   228  		buildO = ""
   229  		buildTarget = ""
   230  		bindPrefix = ""
   231  	}()
   232  	buildN = true
   233  	buildX = true
   234  	buildO = "Asset.framework"
   235  	buildTarget = "ios"
   236  
   237  	buf := new(bytes.Buffer)
   238  	xout = buf
   239  	gopath = filepath.SplitList(goEnv("GOPATH"))[0]
   240  	if goos == "windows" {
   241  		os.Setenv("HOMEDRIVE", "C:")
   242  	}
   243  	cmdBind.flag.Parse([]string{"github.com/F4RD1N/gomobile/asset"})
   244  	if err := runBind(cmdBind); err != nil {
   245  		t.Log(buf.String())
   246  		t.Fatal(err)
   247  	}
   248  }
   249  
   250  func TestBindWithGoModules(t *testing.T) {
   251  	if runtime.GOOS == "android" {
   252  		t.Skipf("gomobile and gobind are not available on %s", runtime.GOOS)
   253  	}
   254  
   255  	dir, err := ioutil.TempDir("", "gomobile-test")
   256  	if err != nil {
   257  		t.Fatal(err)
   258  	}
   259  	defer os.RemoveAll(dir)
   260  
   261  	if out, err := exec.Command("go", "build", "-o="+dir, "github.com/F4RD1N/gomobile/cmd/gobind").CombinedOutput(); err != nil {
   262  		t.Fatalf("%v: %s", err, string(out))
   263  	}
   264  	if out, err := exec.Command("go", "build", "-o="+dir, "github.com/F4RD1N/gomobile/cmd/gomobile").CombinedOutput(); err != nil {
   265  		t.Fatalf("%v: %s", err, string(out))
   266  	}
   267  	path := dir
   268  	if p := os.Getenv("PATH"); p != "" {
   269  		path += string(filepath.ListSeparator) + p
   270  	}
   271  
   272  	for _, target := range []string{"android", "ios"} {
   273  		t.Run(target, func(t *testing.T) {
   274  			switch target {
   275  			case "android":
   276  				androidHome := os.Getenv("ANDROID_HOME")
   277  				if androidHome == "" {
   278  					t.Skip("ANDROID_HOME not found, skipping bind")
   279  				}
   280  				if _, err := androidAPIPath(); err != nil {
   281  					t.Skip("No android API platform found in $ANDROID_HOME, skipping bind")
   282  				}
   283  			case "ios":
   284  				if !xcodeAvailable() {
   285  					t.Skip("Xcode is missing")
   286  				}
   287  			}
   288  
   289  			var out string
   290  			switch target {
   291  			case "android":
   292  				out = filepath.Join(dir, "cgopkg.aar")
   293  			case "ios":
   294  				out = filepath.Join(dir, "Cgopkg.framework")
   295  			}
   296  
   297  			tests := []struct {
   298  				Name string
   299  				Path string
   300  				Dir  string
   301  			}{
   302  				{
   303  					Name: "Absolute Path",
   304  					Path: "github.com/F4RD1N/gomobile/bind/testdata/cgopkg",
   305  				},
   306  				{
   307  					Name: "Relative Path",
   308  					Path: "./bind/testdata/cgopkg",
   309  					Dir:  filepath.Join("..", ".."),
   310  				},
   311  			}
   312  
   313  			for _, tc := range tests {
   314  				tc := tc
   315  				t.Run(tc.Name, func(t *testing.T) {
   316  					cmd := exec.Command(filepath.Join(dir, "gomobile"), "bind", "-target="+target, "-o="+out, tc.Path)
   317  					cmd.Env = append(os.Environ(), "PATH="+path, "GO111MODULE=on")
   318  					cmd.Dir = tc.Dir
   319  					if out, err := cmd.CombinedOutput(); err != nil {
   320  						t.Errorf("gomobile bind failed: %v\n%s", err, string(out))
   321  					}
   322  				})
   323  			}
   324  		})
   325  	}
   326  }