blob: 02a9b7df00df4d97ffc30fee8867c642eb6d5a13 [file] [log] [blame]
// Copyright 2021 The Fuchsia 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 e2e
import (
"context"
"errors"
"flag"
"log"
"os"
"os/exec"
"path/filepath"
"sync"
"testing"
"go.fuchsia.dev/fuchsia/tools/botanist/constants"
"go.fuchsia.dev/fuchsia/tools/fvdl/e2e/e2etest"
"go.fuchsia.dev/fuchsia/tools/lib/ffxutil"
)
var (
aemuDir = flag.String("aemu_dir", "", "Path to AEMU directory")
grpcWebProxyDir = flag.String("grpcwebproxy_dir", "", "Path to grpcwebproxy directory")
deviceLauncherDir = flag.String("device_launcher_dir", "", "Path to device launcher directory")
targetCPU = flag.String("target_cpu", "", "Toolchain used to build Fuchsia image.")
emulatorPath string
fvdl string
deviceLauncher string
grpcwebproxyPath string
fuchsiaBuildDir string
hostToolsDir string
ffx string
ffxInstance *ffxutil.FFXInstance
fvm string
zbi string
kernel string
runtimeDir string
amberFiles string
setupOnce sync.Once
)
func setUp(t *testing.T, intree bool) {
setupOnce.Do(func() {
var err error
deviceLauncher = *deviceLauncherDir
if _, err := os.Stat(deviceLauncher); os.IsNotExist(err) {
t.Fatalf("Invalid vdl path %q err: %s", deviceLauncher, err)
}
if emulatorPath = e2etest.FindFileFromDir(*aemuDir, "emulator"); emulatorPath == "" {
t.Fatalf("Cannot find emulator binary from %q", runtimeDir)
}
if grpcwebproxyPath = e2etest.FindFileFromDir(*grpcWebProxyDir, "grpcwebproxy"); grpcwebproxyPath == "" {
t.Fatalf("Cannot find grpcwebproxy binary from %q", runtimeDir)
}
ex, err := os.Executable()
if err != nil {
t.Fatal(err)
}
exDir := filepath.Dir(ex)
runtimeDir = e2etest.FindDirFromDir(exDir, "fvdl_test_runtime_deps")
if len(runtimeDir) == 0 {
t.Fatalf("Cannot find fvdl_test_runtime_deps binary from %s", exDir)
}
t.Logf("[test info] fuchsia_build_dir %s", exDir)
t.Logf("[test info] fvdl_test_runtime_deps %s", runtimeDir)
hostToolsDir = filepath.Join(runtimeDir, "host_tools")
if _, err := os.Stat(hostToolsDir); os.IsNotExist(err) {
t.Fatalf("Invalid host tools dir %q err: %s", hostToolsDir, err)
}
fvdl = filepath.Join(hostToolsDir, "fvdl")
if _, err := os.Stat(fvdl); os.IsNotExist(err) {
t.Fatal(err)
}
ffx = filepath.Join(hostToolsDir, "ffx")
if _, err := os.Stat(ffx); os.IsNotExist(err) {
t.Fatal(err)
}
fuchsiaBuildDir = filepath.Join(runtimeDir, "images")
if _, err := os.Stat(fuchsiaBuildDir); os.IsNotExist(err) {
t.Fatalf("Invalid fuchsia build dir %q err: %s", fuchsiaBuildDir, err)
}
zbi = filepath.Join(fuchsiaBuildDir, "fuchsia.zbi")
if _, err := os.Stat(zbi); os.IsNotExist(err) {
t.Fatal(err)
}
fvm = filepath.Join(fuchsiaBuildDir, "fvm.blk")
if _, err := os.Stat(fvm); os.IsNotExist(err) {
t.Fatal(err)
}
kernel = filepath.Join(fuchsiaBuildDir, "multiboot.bin")
if _, err := os.Stat(kernel); os.IsNotExist(err) {
t.Fatal(err)
}
packages := filepath.Join(fuchsiaBuildDir, "packages.tar.gz")
if _, err := os.Stat(packages); os.IsNotExist(err) {
t.Fatal(err)
}
if err := os.Mkdir(filepath.Join(runtimeDir, ".jiri_root"), 0o755); err != nil && !os.IsExist(err) {
t.Fatal(err)
}
if err := e2etest.ExtractPackage(packages, fuchsiaBuildDir); err != nil {
t.Fatal(err)
}
amberFiles = filepath.Join(fuchsiaBuildDir, "amber-files")
if intree {
if err := e2etest.GenerateFakeArgsFile(filepath.Join(fuchsiaBuildDir, "args.gn")); err != nil {
t.Fatal(err)
}
if err := e2etest.GenerateFakeImagesJson(filepath.Join(fuchsiaBuildDir, "images.json")); err != nil {
t.Fatal(err)
}
}
if err := e2etest.CreateSSHKeyPairFiles(runtimeDir); err != nil {
t.Fatal(err)
}
})
}
// runVDLWithArgs runs fvdl, if intree, use environment variables to set tools and image path.
// if not intree, images will be downloaded from GCS.
func runVDLWithArgs(ctx context.Context, t *testing.T, args []string, intree bool) string {
testOut, ok := os.LookupEnv("FUCHSIA_TEST_OUTDIR")
if !ok {
testOut = t.TempDir()
}
if err := os.MkdirAll(filepath.Join(testOut, t.Name()), 0o755); err != nil {
t.Fatal(err)
}
// Create a new isolated ffx instance.
ffxInstance, err := ffxutil.NewFFXInstance(ffx, "", os.Environ(), os.Getenv(constants.NodenameEnvKey), os.Getenv(constants.SSHKeyEnvKey), testOut)
if err != nil {
t.Fatal(err)
}
ffxConfigPath := filepath.Join(testOut, "ffx_config.json")
vdlOut := filepath.Join(testOut, t.Name(), "vdl_out")
t.Logf("[test info] writing vdl output to %s", vdlOut)
td := t.TempDir()
t.Cleanup(func() {
killEmu(ctx, t, intree, vdlOut)
if ffxInstance != nil {
if err := ffxInstance.Stop(); err != nil {
t.Logf("FFX didn't stop the running daemon %s", err)
}
}
})
maxTries := 2
for tries := 0; tries < maxTries; tries++ {
cmd := exec.CommandContext(
ctx,
fvdl,
append(
args,
"--vdl-output", vdlOut,
"--emulator-log", filepath.Join(testOut, t.Name(), "emu_log"),
"--amber-unpack-root", filepath.Join(td, "packages"),
"--isolated-ffx-config-path", ffxConfigPath,
)...,
)
if intree {
cmd.Env = append(
os.Environ(),
"FUCHSIA_BUILD_DIR="+fuchsiaBuildDir,
"FUCHSIA_ZBI_COMPRESSION=zstd",
"HOST_OUT_DIR="+hostToolsDir,
"PREBUILT_AEMU_DIR="+mustAbs(t, emulatorPath),
"PREBUILT_GRPCWEBPROXY_DIR="+mustAbs(t, grpcwebproxyPath),
"PREBUILT_VDL_DIR="+mustAbs(t, deviceLauncher),
"FVDL_INVOKER=fvdl_e2e_test",
)
} else {
t.Logf("[test info] setting HOME to: %s", runtimeDir)
// Set $HOME to runtimeDir so that fvdl can find the ssh key files, which are
// expected in $HOME/.ssh/...
cmd.Env = append(os.Environ(), "HOME="+runtimeDir, "FVDL_INVOKER=fvdl_e2e_test")
}
cmd.Dir = td
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
var e *exec.ExitError
if errors.As(err, &e) {
// ExitCode == 1 means something went wrong with the launcher. Don't retry.
if e.ExitCode() == 1 {
t.Fatal(err)
}
}
if tries == maxTries-1 {
t.Fatal(err)
}
log.Printf("Retry launching emulator... got err %s", err)
} else {
break
}
}
return vdlOut
}
func mustAbs(t *testing.T, p string) string {
t.Helper()
abs, err := filepath.Abs(p)
if err != nil {
t.Fatalf("Failed to convert %q to absolute path: %v", p, err)
}
return abs
}
// killEmu shuts down fvdl with action kill command
func killEmu(ctx context.Context, t *testing.T, intree bool, vdlOut string) {
t.Logf("[test info] killing fvdl using proto: %s", vdlOut)
var cmd *exec.Cmd
if intree {
cmd = exec.CommandContext(ctx, fvdl, "kill", "--launched-proto", vdlOut)
cmd.Env = append(
os.Environ(),
"FUCHSIA_BUILD_DIR="+fuchsiaBuildDir,
"FUCHSIA_ZBI_COMPRESSION=zstd",
"HOST_OUT_DIR="+hostToolsDir,
"PREBUILT_AEMU_DIR="+mustAbs(t, emulatorPath),
"PREBUILT_GRPCWEBPROXY_DIR="+mustAbs(t, grpcwebproxyPath),
"PREBUILT_VDL_DIR="+mustAbs(t, deviceLauncher),
"FVDL_INVOKER=fvdl_e2e_test",
)
} else {
cmd = exec.CommandContext(
ctx,
fvdl,
"--sdk",
"kill",
"--launched-proto",
vdlOut,
"-d",
mustAbs(t, filepath.Join(deviceLauncher, "device_launcher")),
)
cmd.Env = append(os.Environ(), "FVDL_INVOKER=fvdl_e2e_test")
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
t.Errorf("shutting down fvdl errored: %w", err)
}
}