blob: 073b8375fbf05d35dd90fa93e6d7ea098c9c46f1 [file] [log] [blame]
// 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 utiltest
import (
"fmt"
"os"
goexec "os/exec"
"strings"
"testing"
"v.io/v23"
"v.io/v23/rpc"
"v.io/x/ref/internal/logger"
"v.io/x/ref/lib/signals"
"v.io/x/ref/services/device/deviced/internal/starter"
"v.io/x/ref/services/device/deviced/internal/versioning"
"v.io/x/ref/services/device/internal/config"
"v.io/x/ref/services/device/internal/suid"
"v.io/x/ref/test"
"v.io/x/ref/test/modules"
)
const (
RedirectEnv = "DEVICE_MANAGER_DONT_REDIRECT_STDOUT_STDERR"
TestEnvVarName = "V23_RANDOM_ENV_VALUE"
NoPairingToken = ""
)
// ExecScript launches the script passed as argument.
var ExecScript = modules.Register(func(env *modules.Env, args ...string) error {
if want, got := 1, len(args); want != got {
logger.Global().Fatalf("ExecScript expected %d arguments, got %d instead", want, got)
}
script := args[0]
osenv := []string{RedirectEnv + "=1"}
if env.Vars["PAUSE_BEFORE_STOP"] == "1" {
osenv = append(osenv, "PAUSE_BEFORE_STOP=1")
}
cmd := goexec.Cmd{
Path: script,
Env: osenv,
Stdin: env.Stdin,
Stderr: env.Stderr,
Stdout: env.Stdout,
}
return cmd.Run()
}, "ExecScript")
// DeviceManager sets up a device manager server. It accepts the name to
// publish the server under as an argument. Additional arguments can optionally
// specify device manager config settings.
var DeviceManager = modules.Register(deviceManagerFunc, "DeviceManager")
func deviceManagerFunc(env *modules.Env, args ...string) error {
ctx, shutdown := test.V23Init()
if len(args) == 0 {
ctx.Fatalf("deviceManager expected at least an argument")
}
publishName := args[0]
args = args[1:]
defer fmt.Fprintf(env.Stdout, "%v terminated\n", publishName)
defer ctx.VI(1).Infof("%v terminated", publishName)
defer shutdown()
// Satisfy the contract described in doc.go by passing the config state
// through to the device manager dispatcher constructor.
configState, err := config.Load()
if err != nil {
ctx.Fatalf("Failed to decode config state: %v", err)
}
// This exemplifies how to override or set specific config fields, if,
// for example, the device manager is invoked 'by hand' instead of via a
// script prepared by a previous version of the device manager.
var pairingToken string
if len(args) > 0 {
if want, got := 4, len(args); want > got {
ctx.Fatalf("expected atleast %d additional arguments, got %d instead: %q", want, got, args)
}
configState.Root, configState.Helper, configState.Origin, configState.CurrentLink = args[0], args[1], args[2], args[3]
if len(args) > 4 {
pairingToken = args[4]
}
}
// We grab the shutdown channel at this point in order to ensure that we
// register a listener for the app cycle manager Stop before we start
// running the device manager service. Otherwise, any device manager
// method that calls Stop on the app cycle manager (e.g. the Stop RPC)
// will precipitate an immediate process exit.
shutdownChan := signals.ShutdownOnSignals(ctx)
listenSpec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
claimableName, stop, err := starter.Start(ctx, starter.Args{
Namespace: starter.NamespaceArgs{
ListenSpec: listenSpec,
},
Device: starter.DeviceArgs{
Name: publishName,
ListenSpec: listenSpec,
ConfigState: configState,
TestMode: strings.HasSuffix(fmt.Sprint(v23.GetPrincipal(ctx).BlessingStore().Default()), "/testdm"),
RestartCallback: func() { fmt.Println("restart handler") },
PairingToken: pairingToken,
},
// TODO(rthellend): Wire up the local mounttable like the real device
// manager, i.e. mount the device manager and the apps on it, and mount
// the local mounttable in the global namespace.
// MountGlobalNamespaceInLocalNamespace: true,
})
if err != nil {
ctx.Errorf("starter.Start failed: %v", err)
return err
}
defer stop()
// Update the namespace roots to remove the server blessing from the
// endpoints. This is needed to be able to publish into the 'global'
// mounttable before we have compatible credentials.
ctx, err = SetNamespaceRootsForUnclaimedDevice(ctx)
if err != nil {
return err
}
// Manually mount the claimable service in the 'global' mounttable.
v23.GetNamespace(ctx).Mount(ctx, "claimable", claimableName, 0)
fmt.Fprintf(env.Stdout, "ready:%d\n", os.Getpid())
<-shutdownChan
if val, present := env.Vars["PAUSE_BEFORE_STOP"]; present && val == "1" {
modules.WaitForEOF(env.Stdin)
}
// TODO(ashankar): Figure out a way to incorporate this check in the test.
// if impl.DispatcherLeaking(dispatcher) {
// ctx.Fatalf("device manager leaking resources")
// }
return nil
}
// This is the same as DeviceManager above, except that it has a different major version number
var DeviceManagerV10 = modules.Register(func(env *modules.Env, args ...string) error {
versioning.CurrentVersion = versioning.Version{10, 0} // Set the version number to 10.0
return deviceManagerFunc(env, args...)
}, "DeviceManagerV10")
func TestMainImpl(m *testing.M) {
isSuidHelper := len(os.Getenv("V23_SUIDHELPER_TEST")) > 0
if modules.IsChildProcess() && !isSuidHelper {
if err := modules.Dispatch(); err != nil {
fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
os.Exit(1)
}
return
}
os.Exit(m.Run())
}
// TestSuidHelper is testing boilerplate for suidhelper that does not
// create a runtime because the suidhelper is not a Vanadium application.
func TestSuidHelperImpl(t *testing.T) {
if os.Getenv("V23_SUIDHELPER_TEST") != "1" {
return
}
logger.Global().VI(1).Infof("TestSuidHelper starting")
if err := suid.Run(os.Environ()); err != nil {
logger.Global().Fatalf("Failed to Run() setuidhelper: %v", err)
}
}