From 5487fc810303f5eb8029752d503d46e73af89d51 Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Thu, 31 Jul 2014 08:25:23 -0400 Subject: [PATCH] go.mobile/bind/java: tests of object reference tracking LGTM=adonovan R=adonovan CC=golang-codereviews https://golang.org/cl/112700044 --- bind/java/SeqTest.java | 64 +++++++++ bind/java/javatest.go | 17 +++ bind/java/test.bash | 28 ++++ bind/java/testpkg/Testpkg.java | 160 +++++++++++++++++++++ bind/java/testpkg/go_testpkg/go_testpkg.go | 99 +++++++++++++ bind/java/testpkg/testpkg.go | 58 ++++++++ 6 files changed, 426 insertions(+) create mode 100644 bind/java/SeqTest.java create mode 100644 bind/java/javatest.go create mode 100755 bind/java/test.bash create mode 100644 bind/java/testpkg/Testpkg.java create mode 100644 bind/java/testpkg/go_testpkg/go_testpkg.go create mode 100644 bind/java/testpkg/testpkg.go diff --git a/bind/java/SeqTest.java b/bind/java/SeqTest.java new file mode 100644 index 0000000..0039b3d --- /dev/null +++ b/bind/java/SeqTest.java @@ -0,0 +1,64 @@ +package go; + +import go.testpkg.Testpkg; + +import junit.framework.TestCase; + +public class SeqTest extends TestCase { + static { + Go.init(null); + } + + public void testAdd() { + long res = Testpkg.instance.Add(3, 4); + assertEquals("Unexpected arithmetic failure", 7, res); + } + + public void testGoRefGC() { + Testpkg.S s = Testpkg.instance.New(); + System.gc(); + Testpkg.instance.GC(); + long collected = Testpkg.instance.NumSCollected(); + assertEquals("Only S should be pinned", 0, collected); + + s = null; + System.gc(); + Testpkg.instance.GC(); + collected = Testpkg.instance.NumSCollected(); + assertEquals("S should be collected", 1, collected); + } + + boolean finalizedAnI; + + private class AnI extends Testpkg.I.Stub { + boolean called; + public void F() { + called = true; + } + @Override + public void finalize() throws Throwable { + finalizedAnI = true; + super.finalize(); + } + } + + public void testJavaRefGC() { + finalizedAnI = false; + AnI obj = new AnI(); + Testpkg.instance.Call(obj); + assertTrue("want F to be called", obj.called); + obj = null; + Testpkg.instance.GC(); + System.gc(); + assertTrue("want obj to be collected", finalizedAnI); + } + + public void testJavaRefKeep() { + finalizedAnI = false; + AnI obj = new AnI(); + Testpkg.instance.Keep(obj); + obj = null; + System.gc(); + assertFalse("want obj to be kept live by Go", finalizedAnI); + } +} diff --git a/bind/java/javatest.go b/bind/java/javatest.go new file mode 100644 index 0000000..3554e1f --- /dev/null +++ b/bind/java/javatest.go @@ -0,0 +1,17 @@ +// Copyright 2014 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. + +//+build ignore + +package main + +import ( + "code.google.com/p/go.mobile/app" + + _ "code.google.com/p/go.mobile/bind/java/testpkg/go_testpkg" +) + +func main() { + app.Run() +} diff --git a/bind/java/test.bash b/bind/java/test.bash new file mode 100755 index 0000000..612c539 --- /dev/null +++ b/bind/java/test.bash @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Copyright 2014 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. + +# TODO(crawshaw): +# This script does not yet run the tests, it just sets them +# up so they can be run by Android Studio. + +set -e + +if [ ! -f test.bash ]; then + echo 'test.bash must be run from $GOPATH/src/code.google.com/p/go.mobile/bind/java' + exit 1 +fi + +if [ -z "$ANDROID_APP" ]; then + echo 'ERROR: Environment variable ANDROID_APP is unset.' + exit 1 +fi + +mkdir -p $ANDROID_APP/src/main/jniLibs/armeabi \ + $ANDROID_APP/src/main/java/go/testpkg +ln -sf $PWD/*.java $ANDROID_APP/src/main/java/go +ln -sf $PWD/testpkg/Testpkg.java $ANDROID_APP/src/main/java/go/testpkg +CGO_ENABLED=1 GOOS=android GOARCH=arm GOARM=7 \ + go build -ldflags="-shared" javatest.go +mv -f javatest $ANDROID_APP/src/main/jniLibs/armeabi/libgojni.so diff --git a/bind/java/testpkg/Testpkg.java b/bind/java/testpkg/Testpkg.java new file mode 100644 index 0000000..e50f800 --- /dev/null +++ b/bind/java/testpkg/Testpkg.java @@ -0,0 +1,160 @@ +// Java Package testpkg is a proxy for talking to a Go program. +// +// File is generated by gobind. Do not edit. +// Or do, I'm a comment, not a cop. +package go.testpkg; + +import go.Seq; + +public final class Testpkg { + public long Add(long x, long y) { + go.Seq _in = new go.Seq(); + go.Seq _out = new go.Seq(); + long _result; + _in.writeInt(x); + _in.writeInt(y); + Seq.send(DESCRIPTOR, CALL_Add, _in, _out); + _result = _out.readInt(); + return _result; + } + + public void Call(I i) { + go.Seq _in = new go.Seq(); + go.Seq _out = new go.Seq(); + _in.writeRef(i.ref()); + Seq.send(DESCRIPTOR, CALL_Call, _in, _out); + } + + public void GC() { + go.Seq _in = new go.Seq(); + go.Seq _out = new go.Seq(); + Seq.send(DESCRIPTOR, CALL_GC, _in, _out); + } + + public interface I extends go.Seq.Object { + public void F(); + + public static abstract class Stub implements I { + static final String DESCRIPTOR = "go.testpkg.I"; + + private final go.Seq.Ref ref; + public Stub() { + ref = go.Seq.createRef(this); + } + + public go.Seq.Ref ref() { return ref; } + + public void call(int code, go.Seq in, go.Seq out) { + switch (code) { + case Proxy.CALL_F: { + // TODO(crawshaw): handle catching a Exception + this.F(); + return; + } + default: + throw new RuntimeException("unknown code: "+ code); + } + } + } + + static final class Proxy implements I { + static final String DESCRIPTOR = Stub.DESCRIPTOR; + + private go.Seq.Ref ref; + + Proxy(go.Seq.Ref ref) { this.ref = ref; } + + public go.Seq.Ref ref() { return ref; } + + public void call(int code, go.Seq in, go.Seq out) { + throw new RuntimeException("cycle: cannot call proxy"); + } + + public void F() { + go.Seq _in = new go.Seq(); + go.Seq _out = new go.Seq(); + Seq.send(DESCRIPTOR, CALL_F, _in, _out); + } + + static final int CALL_F = 0x0a01; + } + } + + public void Keep(I i) { + go.Seq _in = new go.Seq(); + go.Seq _out = new go.Seq(); + _in.writeRef(i.ref()); + Seq.send(DESCRIPTOR, CALL_Keep, _in, _out); + } + + public S New() { + go.Seq _in = new go.Seq(); + go.Seq _out = new go.Seq(); + S _result; + Seq.send(DESCRIPTOR, CALL_New, _in, _out); + _result = new S(_out.readRef()); + return _result; + } + + public long NumSCollected() { + go.Seq _in = new go.Seq(); + go.Seq _out = new go.Seq(); + long _result; + Seq.send(DESCRIPTOR, CALL_NumSCollected, _in, _out); + _result = _out.readInt(); + return _result; + } + + public static final class S implements go.Seq.Object { + private static final String DESCRIPTOR = "go.testpkg.S"; + private static final int CALL_F = 0x0c00; + + private go.Seq.Ref ref; + + private S(go.Seq.Ref ref) { this.ref = ref; } + + public go.Seq.Ref ref() { return ref; } + + public void call(int code, go.Seq in, go.Seq out) { + throw new RuntimeException("cycle: cannot call concrete proxy"); + } + + + public void F() { + go.Seq _in = new go.Seq(); + go.Seq _out = new go.Seq(); + _in.writeRef(ref); + Seq.send(DESCRIPTOR, CALL_F, _in, _out); + } + + @Override public boolean equals(Object o) { + if (o == null || !(o instanceof S)) { + return false; + } + S that = (S)o; + return true; + } + + @Override public int hashCode() { + return java.util.Arrays.hashCode(new Object[] {}); + } + + @Override public String toString() { + // TODO(crawshaw): use String() string if it is defined. + StringBuilder b = new StringBuilder(); + b.append("S").append("{"); + return b.append("}").toString(); + } + + } + + private static final int CALL_Add = 1; + private static final int CALL_Call = 2; + private static final int CALL_GC = 3; + private static final int CALL_Keep = 4; + private static final int CALL_New = 5; + private static final int CALL_NumSCollected = 6; + private static final String DESCRIPTOR = "testpkg"; + + public static Testpkg instance = new Testpkg(); +} diff --git a/bind/java/testpkg/go_testpkg/go_testpkg.go b/bind/java/testpkg/go_testpkg/go_testpkg.go new file mode 100644 index 0000000..c470b42 --- /dev/null +++ b/bind/java/testpkg/go_testpkg/go_testpkg.go @@ -0,0 +1,99 @@ +// Package go_testpkg is an autogenerated binder stub for package testpkg. +// +// File is generated by gobind. Do not edit. +// Or do, I'm a comment, not a cop. +package go_testpkg + +import ( + "code.google.com/p/go.mobile/bind/java/testpkg" + "code.google.com/p/go.mobile/bind/seq" +) + +func proxy_Add(out, in *seq.Buffer) { + param_x := in.ReadInt() + param_y := in.ReadInt() + res := testpkg.Add(param_x, param_y) + // TODO out.WriteNoException(); + out.WriteInt(res) +} + +func proxy_Call(out, in *seq.Buffer) { + var param_i testpkg.I + param_i_ref := in.ReadRef() + if param_i_ref.Num < 0 { + param_i = param_i_ref.Get().(testpkg.I) + } else { + param_i = (*proxyI)(param_i_ref) + } + testpkg.Call(param_i) + // TODO out.WriteNoException(); +} + +func proxy_GC(out, in *seq.Buffer) { + testpkg.GC() + // TODO out.WriteNoException(); +} + +const ( + proxyIDescriptor = "go.testpkg.I" + proxyIFCode = 0x0a01 +) + +type proxyI seq.Ref + +func (p *proxyI) F() { + out := new(seq.Buffer) + //out.WriteRef((*seq.Ref)(p)) + seq.Transact((*seq.Ref)(p), proxyIFCode, out) +} + +func proxy_Keep(out, in *seq.Buffer) { + var param_i testpkg.I + param_i_ref := in.ReadRef() + if param_i_ref.Num < 0 { + param_i = param_i_ref.Get().(testpkg.I) + } else { + param_i = (*proxyI)(param_i_ref) + } + testpkg.Keep(param_i) + // TODO out.WriteNoException(); +} + +func proxy_New(out, in *seq.Buffer) { + res := testpkg.New() + // TODO out.WriteNoException(); + out.WriteGoRef(res) +} + +func proxy_NumSCollected(out, in *seq.Buffer) { + res := testpkg.NumSCollected() + // TODO out.WriteNoException(); + out.WriteInt(res) +} + +const ( + proxySDescriptor = "go.testpkg.S" + proxySFCode = 0x0c00 +) + +type proxyS seq.Ref + +func proxySF(out, in *seq.Buffer) { + ref := in.ReadRef() + v := ref.Get().(*testpkg.S) + v.F() + // TODO out.WriteNoException(); +} + +func init() { + seq.Register(proxySDescriptor, proxySFCode, proxySF) +} + +func init() { + seq.Register("testpkg", 1, proxy_Add) + seq.Register("testpkg", 2, proxy_Call) + seq.Register("testpkg", 3, proxy_GC) + seq.Register("testpkg", 4, proxy_Keep) + seq.Register("testpkg", 5, proxy_New) + seq.Register("testpkg", 6, proxy_NumSCollected) +} diff --git a/bind/java/testpkg/testpkg.go b/bind/java/testpkg/testpkg.go new file mode 100644 index 0000000..278d373 --- /dev/null +++ b/bind/java/testpkg/testpkg.go @@ -0,0 +1,58 @@ +// Package testpkg contains bound functions for testing the cgo-JNI interface. +package testpkg + +import ( + "fmt" + "runtime" + "time" +) + +type I interface { + F() +} + +func Call(i I) { + i.F() +} + +var keep []I + +func Keep(i I) { + keep = append(keep, i) +} + +var numSCollected int + +type S struct { + // *S already has a finalizer, so we need another object + // to count successful collections. + innerObj *int +} + +func (*S) F() { + fmt.Println("called F on *S") +} + +func finalizeInner(*int) { + numSCollected++ +} + +func New() *S { + s := &S{innerObj: new(int)} + runtime.SetFinalizer(s.innerObj, finalizeInner) + return s +} + +func GC() { + runtime.GC() + time.Sleep(10 * time.Millisecond) + runtime.GC() +} + +func Add(x, y int) int { + return x + y +} + +func NumSCollected() int { + return numSCollected +}