v23proxy: test v23proxy between two mojo shells
Change-Id: I2ab9f3e8fb894a351901a530e94d3e0a8d1ac729
diff --git a/Makefile b/Makefile
index bd7d3a8..335c55c 100644
--- a/Makefile
+++ b/Makefile
@@ -59,8 +59,12 @@
build-dart-examples: gen/echo.mojom.dart gen/fortune.mojom.dart
+.PHONY: test
+test: test-unit test-integration
+
# Go-based unit tests
-test: $(MOJO_SHARED_LIB) gen/go/src/mojom/tests/transcoder_testcases/transcoder_testcases.mojom.go gen-vdl
+.PHONY: test-unit
+test-unit: $(MOJO_SHARED_LIB) gen/go/src/mojom/tests/transcoder_testcases/transcoder_testcases.mojom.go gen-vdl
$(call MOGO_TEST,v.io/x/mojo/transcoder/...)
# Note:This file is needed to compile v23proxy.mojom, so we're symlinking it in from $MOJO_SDK.
@@ -90,6 +94,16 @@
$(BUILD_DIR)/echo_server.mojo: gen/go/src/mojom/examples/echo/echo.mojom.go
$(call MOGO_BUILD,examples/echo/server,$@)
+.PHONY: test-integration
+test-integration: $(BUILD_DIR)/test_client.mojo $(BUILD_DIR)/test_server.mojo $(BUILD_DIR)/v23proxy.mojo
+ GOPATH=$(PWD)/go:$(PWD)/gen/go jiri go -profiles=base,$(MOJO_PROFILE) run go/src/v.io/x/mojo/tests/cmd/runtest.go
+
+$(BUILD_DIR)/test_client.mojo: go/src/v.io/x/mojo/tests/client/test_client.go gen/go/src/mojom/tests/end_to_end_test/end_to_end_test.mojom.go gen/go/src/mojom/v23proxy/v23proxy.mojom.go
+ $(call MOGO_BUILD,v.io/x/mojo/tests/client,$@)
+
+$(BUILD_DIR)/test_server.mojo: go/src/v.io/x/mojo/tests/server/test_server.go gen/go/src/mojom/tests/end_to_end_test/end_to_end_test.mojom.go
+ $(call MOGO_BUILD,v.io/x/mojo/tests/server,$@)
+
gen/go/src/mojom/examples/echo/echo.mojom.go: mojom/mojom/examples/echo.mojom | mojo-env-check
$(call MOJOM_GEN,$<,mojom,gen,go)
gofmt -w $@
@@ -110,7 +124,7 @@
gen/fortune.mojom.dart: mojom/mojom/examples/fortune.mojom | mojo-env-check
$(call MOJOM_GEN,$<,mojom,dart-examples/fortune/lib/gen,dart)
-$(BUILD_DIR)/v23proxy.mojo: gen/go/src/mojom/v23proxy/v23proxy.mojom.go | mojo-env-check
+$(BUILD_DIR)/v23proxy.mojo: $(shell find $(PWD)/go/src/v.io/x/mojo/proxy -name *.go) | mojo-env-check
$(call MOGO_BUILD,v.io/x/mojo/proxy,$@)
gen/go/src/mojo/public/interfaces/bindings/mojom_types/mojom_types.mojom.go: mojom/mojo/public/interfaces/bindings/mojom_types.mojom | mojo-env-check
@@ -128,6 +142,10 @@
$(call MOJOM_GEN,$<,mojom,gen,go)
gofmt -w $@
+gen/go/src/mojom/tests/end_to_end_test/end_to_end_test.mojom.go: mojom/mojom/tests/end_to_end_test.mojom | mojo-env-check
+ $(call MOJOM_GEN,$<,mojom,gen,go)
+ gofmt -w $@
+
gen/v23proxy.mojom.dart: mojom/mojom/v23proxy.mojom packages gen/mojo/public/interfaces/bindings/mojom_types/mojom_types.mojom.dart | mojo-env-check
$(call MOJOM_GEN,$<,mojom,lib/gen,dart)
# TODO(nlacasse): mojom_bindings_generator creates bad symlinks on dart
diff --git a/go/src/v.io/x/mojo/proxy/fake_service.go b/go/src/v.io/x/mojo/proxy/fake_service.go
index 78a86df..85b21b2 100644
--- a/go/src/v.io/x/mojo/proxy/fake_service.go
+++ b/go/src/v.io/x/mojo/proxy/fake_service.go
@@ -9,19 +9,17 @@
"log"
"strings"
- "v.io/v23/context"
- "v.io/v23/rpc"
-
"mojo/public/go/application"
"mojo/public/go/bindings"
"mojo/public/go/system"
+ "mojo/public/interfaces/bindings/mojom_types"
+ "mojo/public/interfaces/bindings/service_describer"
+ "v.io/v23/context"
+ "v.io/v23/rpc"
"v.io/v23/vdl"
"v.io/v23/vdlroot/signature"
"v.io/x/mojo/transcoder"
-
- "mojo/public/interfaces/bindings/mojom_types"
- "mojo/public/interfaces/bindings/service_describer"
)
// As long as fakeService meets the Invoker interface, it is allowed to pass as
diff --git a/go/src/v.io/x/mojo/proxy/proxy.go b/go/src/v.io/x/mojo/proxy/proxy.go
index c0d1179..3550746 100644
--- a/go/src/v.io/x/mojo/proxy/proxy.go
+++ b/go/src/v.io/x/mojo/proxy/proxy.go
@@ -7,20 +7,19 @@
import (
"fmt"
+ "mojo/public/go/application"
+ "mojo/public/go/bindings"
+ "mojo/public/go/system"
+ "mojo/public/interfaces/bindings/mojom_types"
+
+ "mojom/v23proxy"
+
"v.io/v23"
"v.io/v23/context"
"v.io/v23/options"
"v.io/v23/rpc"
"v.io/v23/security"
"v.io/v23/vdl"
-
- "mojom/v23proxy"
-
- "mojo/public/go/application"
- "mojo/public/go/bindings"
- "mojo/public/go/system"
- "mojo/public/interfaces/bindings/mojom_types"
-
"v.io/x/mojo/transcoder"
_ "v.io/x/ref/runtime/factories/roaming"
)
diff --git a/go/src/v.io/x/mojo/tests/client/test_client.go b/go/src/v.io/x/mojo/tests/client/test_client.go
new file mode 100644
index 0000000..603c6cc
--- /dev/null
+++ b/go/src/v.io/x/mojo/tests/client/test_client.go
@@ -0,0 +1,163 @@
+// Copyright 2015 The Vanadium 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 (
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "reflect"
+ "runtime"
+ "strings"
+ "testing"
+
+ "mojo/public/go/application"
+ "mojo/public/go/bindings"
+ "mojo/public/go/system"
+
+ "mojom/tests/end_to_end_test"
+
+ v23 "v.io/x/mojo/client"
+ "v.io/x/mojo/tests/expected"
+)
+
+//#include "mojo/public/c/system/types.h"
+import "C"
+
+func init() {
+ // Add flag placeholders to suppress warnings on unhandled mojo flags.
+ flag.String("child-connection-id", "", "")
+ flag.String("platform-channel-handle-info", "", "")
+}
+
+func TestSimple(t *testing.T, ctx application.Context) {
+ proxy := createProxy(ctx)
+ defer proxy.Close_Proxy()
+
+ value, err := proxy.Simple(expected.SimpleRequestA)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if value != expected.SimpleResponseValue {
+ t.Errorf("expected %v, but got %v", expected.SimpleResponseValue, value)
+ }
+}
+
+func TestMultiArgs(t *testing.T, ctx application.Context) {
+ proxy := createProxy(ctx)
+ defer proxy.Close_Proxy()
+
+ x, y, err := proxy.MultiArgs(expected.MultiArgsRequestA, expected.MultiArgsRequestB, expected.MultiArgsRequestC, expected.MultiArgsRequestD)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(x, expected.MultiArgsResponseX) {
+ t.Errorf("expected %v, but got %v", expected.MultiArgsResponseX, x)
+ }
+ if y != expected.MultiArgsResponseY {
+ t.Errorf("expected %v, but got %v", expected.MultiArgsResponseY, y)
+ }
+}
+
+func TestNoReturn(t *testing.T, ctx application.Context) {
+ proxy := createProxy(ctx)
+ defer proxy.Close_Proxy()
+
+ err := proxy.NoReturn()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestReuseProxy(t *testing.T, ctx application.Context) {
+ fmt.Printf("in test reuse\n")
+ proxy := createProxy(ctx)
+ defer proxy.Close_Proxy()
+
+ value, err := proxy.Simple(expected.SimpleRequestA)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if value != expected.SimpleResponseValue {
+ t.Errorf("expected %v, but got %v", expected.SimpleResponseValue, value)
+ }
+ fmt.Printf("about to call second f\n")
+ x, y, err := proxy.MultiArgs(expected.MultiArgsRequestA, expected.MultiArgsRequestB, expected.MultiArgsRequestC, expected.MultiArgsRequestD)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fmt.Printf("called second f\n")
+ if !reflect.DeepEqual(x, expected.MultiArgsResponseX) {
+ t.Errorf("expected %v, but got %v", expected.MultiArgsResponseX, x)
+ }
+ if y != expected.MultiArgsResponseY {
+ t.Errorf("expected %v, but got %v", expected.MultiArgsResponseY, y)
+ }
+}
+
+func createProxy(ctx application.Context) *end_to_end_test.V23ProxyTest_Proxy {
+ // Parse arguments. Note: May panic if not enough args are given.
+ remoteName := ctx.Args()[1]
+
+ r, p := end_to_end_test.CreateMessagePipeForV23ProxyTest()
+ v23.ConnectToRemoteService(ctx, &r, remoteName)
+ return end_to_end_test.NewV23ProxyTestProxy(p, bindings.GetAsyncWaiter())
+}
+
+type TestClientDelegate struct{}
+
+func funcName(f func(*testing.T, application.Context)) string {
+ qualified := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
+ return qualified[strings.LastIndex(qualified, ".")+1:]
+}
+func convertTests(testFuncs []func(*testing.T, application.Context), ctx application.Context) []testing.InternalTest {
+ tests := make([]testing.InternalTest, len(testFuncs))
+ for i, _ := range testFuncs {
+ f := testFuncs[i]
+ tests[i] = testing.InternalTest{
+ Name: funcName(f),
+ F: func(t *testing.T) { f(t, ctx) },
+ }
+ }
+ return tests
+}
+
+func (delegate *TestClientDelegate) Initialize(ctx application.Context) {
+ log.Printf("TestClientDelegate.Initialize...")
+
+ tests := []func(*testing.T, application.Context){
+ TestSimple, TestMultiArgs, TestNoReturn, TestReuseProxy,
+ }
+
+ matchAllTests := func(pat, str string) (bool, error) { return true, nil }
+ exitCode := testing.MainStart(matchAllTests, convertTests(tests, ctx), nil, nil).Run()
+ if exitCode == 0 {
+ fmt.Printf("%s\n", expected.SuccessMessage)
+ } else {
+ fmt.Printf("%s\n", expected.FailureMessage)
+ }
+
+ ctx.Close()
+ os.Exit(exitCode)
+}
+
+func (delegate *TestClientDelegate) AcceptConnection(connection *application.Connection) {
+ log.Printf("TestClientDelegate.AcceptConnection...")
+ connection.Close()
+}
+
+func (delegate *TestClientDelegate) Quit() {
+ log.Printf("TestClientDelegate.Quit...")
+}
+
+//export MojoMain
+func MojoMain(handle C.MojoHandle) C.MojoResult {
+ application.Run(&TestClientDelegate{}, system.MojoHandle(handle))
+ return C.MOJO_RESULT_OK
+}
+
+func main() {
+}
diff --git a/go/src/v.io/x/mojo/tests/cmd/runtest.go b/go/src/v.io/x/mojo/tests/cmd/runtest.go
new file mode 100644
index 0000000..8b694db
--- /dev/null
+++ b/go/src/v.io/x/mojo/tests/cmd/runtest.go
@@ -0,0 +1,74 @@
+// Copyright 2015 The Vanadium 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 (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+
+ "v.io/x/mojo/tests/expected"
+ "v.io/x/mojo/tests/util"
+)
+
+func main() {
+ wd, err := os.Getwd()
+ if err != nil {
+ panic(err)
+ }
+ v23proxy, err := util.StartV23Proxy(wd)
+ if err != nil {
+ panic(err)
+ }
+ endpoint, err := v23proxy.Endpoint()
+ if err != nil {
+ panic(err)
+ }
+ name := endpoint + "//https://mojo.v.io/test_server.mojo/mojo::v23proxy::tests::V23ProxyTest"
+ if err := runTestClient(wd, name); err != nil {
+ fmt.Fprintf(os.Stderr, "%s", err)
+ os.Exit(2)
+ }
+ if err := v23proxy.Stop(); err != nil {
+ panic(err)
+ }
+}
+
+func runTestClient(v23ProxyRoot, endpoint string) error {
+ cmd := util.RunMojoShellForV23ProxyTests("test_client.mojo", v23ProxyRoot, []string{endpoint})
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return err
+ }
+ var stdoutBuf bytes.Buffer
+ go func() {
+ io.Copy(os.Stdout, io.TeeReader(stdout, &stdoutBuf))
+ }()
+ stderr, err := cmd.StderrPipe()
+ if err != nil {
+ return err
+ }
+ go func() {
+ io.Copy(os.Stderr, stderr)
+ }()
+
+ if err := cmd.Run(); err != nil {
+ return err
+ }
+
+ scanner := bufio.NewScanner(bytes.NewReader(stdoutBuf.Bytes()))
+ for scanner.Scan() {
+ if strings.HasSuffix(scanner.Text(), expected.SuccessMessage) {
+ return nil
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ return err
+ }
+ return fmt.Errorf("TESTS FAILED")
+}
diff --git a/go/src/v.io/x/mojo/tests/expected/expected.go b/go/src/v.io/x/mojo/tests/expected/expected.go
new file mode 100644
index 0000000..9ba0188
--- /dev/null
+++ b/go/src/v.io/x/mojo/tests/expected/expected.go
@@ -0,0 +1,24 @@
+// Copyright 2015 The Vanadium 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 expected
+
+import (
+ "mojom/tests/end_to_end_test"
+)
+
+var (
+ SimpleRequestA int32 = 123
+ SimpleResponseValue = "TheValue"
+
+ MultiArgsRequestA = true
+ MultiArgsRequestB = []float32{1, 2, 3}
+ MultiArgsRequestC = map[string]uint8{"X": 1, "Y": 2}
+ MultiArgsRequestD = end_to_end_test.AStruct{3, 300, 129}
+ MultiArgsResponseX = &end_to_end_test.AUnionB{Value: "TheUnion"}
+ MultiArgsResponseY = "yresponse"
+
+ SuccessMessage = "ALL TESTS PASSED"
+ FailureMessage = "Failed"
+)
diff --git a/go/src/v.io/x/mojo/tests/server/test_server.go b/go/src/v.io/x/mojo/tests/server/test_server.go
new file mode 100644
index 0000000..9462829
--- /dev/null
+++ b/go/src/v.io/x/mojo/tests/server/test_server.go
@@ -0,0 +1,104 @@
+// Copyright 2015 The Vanadium 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"
+ "log"
+ "reflect"
+
+ "mojo/public/go/application"
+ "mojo/public/go/bindings"
+ "mojo/public/go/system"
+
+ "mojom/tests/end_to_end_test"
+
+ "v.io/x/mojo/tests/expected"
+)
+
+//#include "mojo/public/c/system/types.h"
+import "C"
+
+type V23ProxyTestImpl struct{}
+
+func (i *V23ProxyTestImpl) Simple(a int32) (value string, err error) {
+ if a != expected.SimpleRequestA {
+ return "", fmt.Errorf("expected %v, but got %v", expected.SimpleRequestA, a)
+ }
+ return expected.SimpleResponseValue, nil
+}
+
+func (i *V23ProxyTestImpl) MultiArgs(a bool, b []float32, c map[string]uint8, d end_to_end_test.AStruct) (x end_to_end_test.AUnion, y string, err error) {
+ if a != expected.MultiArgsRequestA {
+ return nil, "", fmt.Errorf("expected %v, but got %v", expected.MultiArgsRequestA, a)
+ }
+ if !reflect.DeepEqual(b, expected.MultiArgsRequestB) {
+ return nil, "", fmt.Errorf("expected %v, but got %v", expected.MultiArgsRequestB, b)
+ }
+ if !reflect.DeepEqual(c, expected.MultiArgsRequestC) {
+ return nil, "", fmt.Errorf("expected %v, but got %v", expected.MultiArgsRequestC, c)
+ }
+ if !reflect.DeepEqual(d, expected.MultiArgsRequestD) {
+ return nil, "", fmt.Errorf("expected %v, but got %v", expected.MultiArgsRequestD, d)
+ }
+ return expected.MultiArgsResponseX, expected.MultiArgsResponseY, nil
+}
+
+func (i *V23ProxyTestImpl) NoReturn() error {
+ // TODO(bprosnitz) The test should fail if the message is not received.
+ return nil
+}
+
+type V23ProxyTestServerDelegate struct {
+ factory V23ProxyTestFactory
+}
+
+type V23ProxyTestFactory struct {
+ stubs []*bindings.Stub
+}
+
+func (delegate *V23ProxyTestServerDelegate) Initialize(context application.Context) {
+ log.Printf("V23ProxyTestServerDelegate.Initialize...")
+}
+
+func (factory *V23ProxyTestFactory) Create(request end_to_end_test.V23ProxyTest_Request) {
+ log.Printf("V23ProxyTestServer's V23ProxyTestFactory.Create...")
+ stub := end_to_end_test.NewV23ProxyTestStub(request, &V23ProxyTestImpl{}, bindings.GetAsyncWaiter())
+ factory.stubs = append(factory.stubs, stub)
+ go func() {
+ for {
+ if err := stub.ServeRequest(); err != nil {
+ connectionError, ok := err.(*bindings.ConnectionError)
+ if !ok || !connectionError.Closed() {
+ log.Println(err)
+ }
+ break
+ }
+ }
+ }()
+}
+
+func (delegate *V23ProxyTestServerDelegate) AcceptConnection(connection *application.Connection) {
+ log.Printf("RemoteEchoServerDelegate.AcceptConnection...")
+ connection.ProvideServicesWithDescriber(
+ &end_to_end_test.V23ProxyTest_ServiceFactory{&delegate.factory},
+ )
+}
+
+func (delegate *V23ProxyTestServerDelegate) Quit() {
+ log.Printf("V23ProxyTestServerDelegate.Quit...")
+ for _, stub := range delegate.factory.stubs {
+ stub.Close()
+ }
+}
+
+//export MojoMain
+func MojoMain(handle C.MojoHandle) C.MojoResult {
+ application.Run(&V23ProxyTestServerDelegate{}, system.MojoHandle(handle))
+ return C.MOJO_RESULT_OK
+}
+
+func main() {
+}
diff --git a/go/src/v.io/x/mojo/tests/util/mojo_shell_runner.go b/go/src/v.io/x/mojo/tests/util/mojo_shell_runner.go
new file mode 100644
index 0000000..8cee582
--- /dev/null
+++ b/go/src/v.io/x/mojo/tests/util/mojo_shell_runner.go
@@ -0,0 +1,84 @@
+// Copyright 2015 The Vanadium 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 util
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+
+ "v.io/jiri/jiri"
+ "v.io/jiri/profiles"
+ "v.io/x/lib/cmdline"
+ "v.io/x/lib/timing"
+)
+
+func RunMojoShell(mojoUrl, configFile string, configAliases map[string]string, argsFor map[string][]string, target profiles.Target) *exec.Cmd {
+ // ensure the profiles are loaded
+ jirix, err := jiri.NewX(&cmdline.Env{Timer: timing.NewTimer("root")})
+ if err != nil {
+ panic(err)
+ }
+ _, err = profiles.NewConfigHelper(jirix, profiles.UseProfiles, filepath.Join(jirix.Root, ".jiri_v23_profiles"))
+ if err != nil {
+ panic(err)
+ }
+
+ env := profiles.EnvFromProfile(target, mojoProfileName())
+ var mojoDevtools, mojoShell, mojoServices string
+ for _, e := range env {
+ parts := strings.SplitN(e, "=", 2)
+ switch parts[0] {
+ case "MOJO_DEVTOOLS":
+ mojoDevtools = parts[1]
+ case "MOJO_SHELL":
+ mojoShell = parts[1]
+ case "MOJO_SERVICES":
+ mojoServices = parts[1]
+ }
+ }
+ args := []string{
+ mojoUrl,
+ "--config-file", configFile,
+ "--shell-path", mojoShell,
+ "--enable-multiprocess"}
+ if target.OS() == "android" {
+ args = append(args, "--android")
+ args = append(args, "--origin", mojoServices)
+ }
+ for alias, value := range configAliases {
+ args = append(args, "--config-alias", fmt.Sprintf("%s=%s", alias, value))
+ }
+ for key, value := range argsFor {
+ args = append(args, fmt.Sprintf("--args-for=%s %s", key, strings.Join(value, " ")))
+ }
+ return exec.Command(filepath.Join(mojoDevtools, "mojo_run"), args...)
+}
+
+func RunMojoShellForV23ProxyTests(mojoFile, v23ProxyRoot string, args []string) *exec.Cmd {
+ configFile := filepath.Join(v23ProxyRoot, "mojoconfig")
+ mojoUrl := fmt.Sprintf("https://mojo.v.io/%s", mojoFile)
+ buildDir := filepath.Join(v23ProxyRoot, "gen", "mojo", "linux_amd64")
+ configAliases := map[string]string{
+ "V23PROXY_DIR": v23ProxyRoot,
+ "V23PROXY_BUILD_DIR": buildDir,
+ }
+ argsFor := map[string][]string{
+ mojoUrl: args,
+ "mojo:dart_content_handler": []string{"--enable-strict-mode"},
+ }
+ target := profiles.DefaultTarget()
+ return RunMojoShell(mojoUrl, configFile, configAliases, argsFor, target)
+}
+
+func mojoProfileName() string {
+ if val, ok := os.LookupEnv("USE_MOJO_DEV_PROFILE"); ok && val == "true" {
+ fmt.Printf("Using dev profile\n")
+ return "mojo-dev"
+ }
+ return "mojo"
+}
diff --git a/go/src/v.io/x/mojo/tests/util/v23_proxy_controller.go b/go/src/v.io/x/mojo/tests/util/v23_proxy_controller.go
new file mode 100644
index 0000000..e27c033
--- /dev/null
+++ b/go/src/v.io/x/mojo/tests/util/v23_proxy_controller.go
@@ -0,0 +1,118 @@
+// Copyright 2015 The Vanadium 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 util
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "strconv"
+ "strings"
+ "sync"
+ "syscall"
+)
+
+func StartV23Proxy(v23ProxyRoot string) (*V23ProxyController, error) {
+ cmd := RunMojoShellForV23ProxyTests("v23proxy.mojo", v23ProxyRoot, []string{})
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+ stderr, err := cmd.StderrPipe()
+ if err != nil {
+ return nil, err
+ }
+ go func() {
+ io.Copy(os.Stderr, stderr)
+ }()
+ // A lock is put in home for the url response cache. Change HOME for v23proxy, since
+ // two mojo shells will be run.
+ tempHome, err := ioutil.TempDir("", "")
+ if err != nil {
+ return nil, err
+ }
+ cmd.Env = append(cmd.Env, "HOME="+tempHome)
+ if err := cmd.Start(); err != nil {
+ return nil, err
+ }
+ return &V23ProxyController{
+ cmd: cmd,
+ stdout: stdout,
+ tempHome: tempHome,
+ }, nil
+}
+
+type V23ProxyController struct {
+ cmd *exec.Cmd
+ stdout io.ReadCloser
+ endpointLock sync.Mutex
+ endpoint string // empty until the endpoint is read
+ tempHome string
+}
+
+func (v *V23ProxyController) Stop() error {
+ os.Remove(v.tempHome)
+ childPids, err := getChildProcessPids(v.cmd.Process.Pid)
+ if err != nil {
+ return err
+ }
+ if err := v.cmd.Process.Signal(syscall.SIGTERM); err != nil {
+ return err
+ }
+ for _, pid := range childPids {
+ syscall.Kill(pid, syscall.SIGTERM)
+ }
+ return nil
+}
+
+func (v *V23ProxyController) Endpoint() (string, error) {
+ v.endpointLock.Lock()
+ defer v.endpointLock.Unlock()
+
+ if v.endpoint != "" {
+ return v.endpoint, nil
+ }
+
+ scanner := bufio.NewScanner(v.stdout)
+ const prefix = "Listening at: "
+ for scanner.Scan() {
+ if strings.HasPrefix(scanner.Text(), prefix) {
+ v.endpoint = strings.TrimPrefix(scanner.Text(), prefix)
+ return v.endpoint, nil
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ return "", err
+ }
+
+ return "", fmt.Errorf("unexpected EOF when looking for endpoint")
+}
+
+func getChildProcessPids(parentPid int) ([]int, error) {
+ cmd := exec.Command("ps", []string{"h", "--ppid", fmt.Sprintf("%d", parentPid), "-o", "pid"}...)
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+ scanner := bufio.NewScanner(stdout)
+ var pids []int
+ if err := cmd.Start(); err != nil {
+ return nil, err
+ }
+ for scanner.Scan() {
+ if len(scanner.Text()) == 0 {
+ continue
+ }
+ pid, err := strconv.ParseInt(strings.Trim(scanner.Text(), " "), 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ pids = append(pids, int(pid))
+ }
+ return pids, cmd.Wait()
+}
diff --git a/mojom/mojom/tests/end_to_end_test.mojom b/mojom/mojom/tests/end_to_end_test.mojom
new file mode 100644
index 0000000..a5b3a1d
--- /dev/null
+++ b/mojom/mojom/tests/end_to_end_test.mojom
@@ -0,0 +1,23 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+module mojo.v23proxy.tests;
+
+struct AStruct{
+ uint8 x;
+ int16 y;
+ uint8 z;
+};
+
+union AUnion{
+ uint8 a;
+ string b;
+};
+
+[ServiceName="mojo::v23proxy::tests::V23ProxyTest"]
+interface V23ProxyTest {
+ Simple(int32 a) => (string value);
+ MultiArgs(bool a, array<float> b, map<string, uint8> c, AStruct d) => (AUnion x, string y);
+ NoReturn();
+};