| // Copyright 2020 The Chromium OS 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(crbug/1142830) - Change all native terms when Android's subcomponents |
| // name are changed. |
| // Modify the filename to match the substitute term. |
| |
| package arc |
| |
| import ( |
| "context" |
| "time" |
| |
| "chromiumos/tast/common/testexec" |
| "chromiumos/tast/ctxutil" |
| "chromiumos/tast/errors" |
| "chromiumos/tast/local/arc" |
| "chromiumos/tast/local/chrome" |
| "chromiumos/tast/local/sysutil" |
| "chromiumos/tast/testing" |
| ) |
| |
| type nativeBridgeConfig struct { |
| // ARM executables to be run. |
| armExecutables []string |
| // Binary translation library to be used. |
| binaryTranslationLibrary string |
| // Extra arguments to be passed to Chrome.New(). |
| chromeExtraArgs []string |
| } |
| |
| const ( |
| // Android native ARM executables expected to be runnable via native bridge. |
| // They are generated by ArcNativeBridgeTestHelper in the Android source tree. |
| armExec = "hello_world_arm" |
| arm64Exec = "hello_world_arm64" |
| // Names of the binary translation libraries. |
| houdiniLib = "libhoudini.so" |
| ndkTranslationLib = "libndk_translation.so" |
| // Chrome feature for switching the binary translation library on a dual-translator board. |
| nativeBridgeExperimentFeature = "ArcNativeBridgeExperiment" |
| ) |
| |
| func init() { |
| testing.AddTest(&testing.Test{ |
| Func: NativeBridge, |
| Desc: "Checks whether native bridge is properly set up for ARCVM", |
| Contacts: []string{"youkichihosoi@chromium.org", "arcvm-eng@google.com"}, |
| SoftwareDeps: []string{"android_vm", "chrome"}, |
| Attr: []string{"group:mainline", "informational"}, |
| Params: []testing.Param{{ |
| Name: "houdini", |
| Val: nativeBridgeConfig{ |
| armExecutables: []string{armExec}, |
| binaryTranslationLibrary: houdiniLib, |
| chromeExtraArgs: []string{"--disable-features=" + nativeBridgeExperimentFeature}, |
| }, |
| ExtraData: []string{armExec}, |
| ExtraSoftwareDeps: []string{"houdini"}, |
| }, { |
| Name: "houdini64", |
| Val: nativeBridgeConfig{ |
| armExecutables: []string{arm64Exec}, |
| binaryTranslationLibrary: houdiniLib, |
| chromeExtraArgs: []string{"--disable-features=" + nativeBridgeExperimentFeature}, |
| }, |
| ExtraData: []string{arm64Exec}, |
| ExtraSoftwareDeps: []string{"houdini64"}, |
| }, { |
| Name: "ndk_translation", |
| Val: nativeBridgeConfig{ |
| armExecutables: []string{armExec}, |
| binaryTranslationLibrary: ndkTranslationLib, |
| chromeExtraArgs: []string{"--enable-features=" + nativeBridgeExperimentFeature}, |
| }, |
| ExtraData: []string{armExec}, |
| ExtraSoftwareDeps: []string{"ndk_translation"}, |
| }, { |
| Name: "ndk_translation64", |
| Val: nativeBridgeConfig{ |
| armExecutables: []string{arm64Exec}, |
| binaryTranslationLibrary: ndkTranslationLib, |
| chromeExtraArgs: []string{"--enable-features=" + nativeBridgeExperimentFeature}, |
| }, |
| ExtraData: []string{arm64Exec}, |
| ExtraSoftwareDeps: []string{"ndk_translation64"}, |
| }}, |
| Timeout: 7 * time.Minute, |
| }) |
| } |
| |
| func NativeBridge(ctx context.Context, s *testing.State) { |
| cfg := s.Param().(nativeBridgeConfig) |
| |
| // Shorten the total context to make room for cleanup jobs. |
| cleanupCtx := ctx |
| ctx, cancel := ctxutil.Shorten(ctx, 30*time.Second) |
| defer cancel() |
| |
| cr, err := chrome.New(ctx, chrome.ARCEnabled(), chrome.ExtraArgs(cfg.chromeExtraArgs...)) |
| if err != nil { |
| s.Fatal("Failed to connect to Chrome: ", err) |
| } |
| defer func() { |
| if err := cr.Close(cleanupCtx); err != nil { |
| s.Fatal("Failed to close Chrome: ", err) |
| } |
| }() |
| |
| a, err := arc.New(ctx, s.OutDir()) |
| if err != nil { |
| s.Fatal("Failed to start ARC: ", err) |
| } |
| defer func() { |
| if a != nil { |
| a.Close(ctx) |
| } |
| }() |
| |
| // Check whether binfmt_misc has been successfully unmounted. |
| ms, err := mountInfoForARCVM(ctx, a) |
| if err != nil { |
| s.Fatal("Failed to get mount info for ARCVM: ", err) |
| } |
| const binfmtMiscPath = "/proc/sys/fs/binfmt_misc" |
| for _, m := range ms { |
| if m.MountPath == binfmtMiscPath { |
| s.Fatalf("Failure: %q is not unmounted", binfmtMiscPath) |
| } |
| } |
| |
| // Check whether the property for the binary translation library has been properly set. |
| const nativeBridgeProp = "ro.boot.native_bridge" |
| nb, err := a.GetProp(ctx, nativeBridgeProp) |
| if err != nil { |
| s.Fatalf("Failed to getprop %q: %v", nativeBridgeProp, err) |
| } |
| nbExpected := cfg.binaryTranslationLibrary |
| if nb != nbExpected { |
| s.Fatalf("Failure: the property %q is set to %q; expected to be %q", nativeBridgeProp, nb, nbExpected) |
| } |
| |
| // Check whether ARM executables can be successfully run. |
| for _, exec := range cfg.armExecutables { |
| if err := pushAndExecute(ctx, a, s.DataPath(exec)); err != nil { |
| s.Fatalf("Failed to run ARM executable %q: %v", exec, err) |
| } |
| } |
| } |
| |
| // pushAndExecute pushes an executable to Android's temporary directory and executes it. |
| func pushAndExecute(ctx context.Context, a *arc.ARC, execPath string) (retErr error) { |
| tmpExecPath, err := a.PushFileToTmpDir(ctx, execPath) |
| if err != nil { |
| return errors.Wrapf(err, "failed to push %q to tmpdir", execPath) |
| } |
| defer func() { |
| if err := a.Command(ctx, "rm", "-f", tmpExecPath).Run(testexec.DumpLogOnError); err != nil { |
| if retErr == nil { |
| retErr = errors.Wrapf(err, "failed to remove %q", tmpExecPath) |
| } else { |
| testing.ContextLogf(ctx, "Failed to remove %q: %v", tmpExecPath, err) |
| } |
| } |
| }() |
| |
| if err := a.Command(ctx, "chmod", "0755", tmpExecPath).Run(testexec.DumpLogOnError); err != nil { |
| return errors.Wrapf(err, "failed to change the permission of %q", tmpExecPath) |
| } |
| if err := a.Command(ctx, tmpExecPath).Run(testexec.DumpLogOnError); err != nil { |
| return errors.Wrapf(err, "failed to execute %q", tmpExecPath) |
| } |
| return nil |
| } |
| |
| // mountInfoForARCVM returns a list of mount point info for ARCVM. |
| func mountInfoForARCVM(ctx context.Context, a *arc.ARC) ([]sysutil.MountInfo, error) { |
| const mountInfoPath = "/proc/self/mountinfo" |
| mi, err := a.ReadFile(ctx, mountInfoPath) |
| if err != nil { |
| return nil, errors.Wrapf(err, "failed to read mount info file %q", mountInfoPath) |
| } |
| return sysutil.ParseMountInfo(mi) |
| } |