blob: c7ae9bc1c173c810e89a5d30b3fcd07d05e9fa62 [file] [log] [blame]
// 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.
package arc
import (
"context"
"io/ioutil"
"path/filepath"
"strings"
"time"
"chromiumos/tast/common/android/adb"
"chromiumos/tast/common/testexec"
"chromiumos/tast/ctxutil"
"chromiumos/tast/errors"
"chromiumos/tast/local/arc"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/graphics"
"chromiumos/tast/testing"
)
const (
apkCTS9 = "ArcDEQPAndroidCTS-9.0r9_20191126.apk"
apkCTS11 = "ArcDEQPAndroidCTS-11.0r1_20200924.apk"
testCaseDataDir = "deqp_tests"
packageName = "com.drawelements.deqp"
activityName = "android.app.NativeActivity"
)
// deqpTests contain the filenames of the tests cases we want to run the
// dEQP on. These caselist files have been obtained by the official Android
// CTS suites: https://source.android.com/compatibility/cts/downloads.html
// TODO(morg): Set up a DEQP "full" test suite for nightly builds.
var deqpTests = []string{
filepath.Join(testCaseDataDir, "gles3-multisample.txt"),
filepath.Join(testCaseDataDir, "gles31-multisample.txt"),
}
func init() {
testing.AddTest(&testing.Test{
Func: DEQP,
Desc: "Runs a subset of the DEQP test suite via the android CTS-provided apk binary",
Contacts: []string{"morg@chromium.org", "arc-eng@google.com"},
Data: deqpTests,
SoftwareDeps: []string{"chrome"},
Fixture: "arcBooted",
Params: []testing.Param{{
ExtraAttr: []string{"group:mainline", "informational"},
ExtraSoftwareDeps: []string{"android_p"},
ExtraData: []string{apkCTS9},
Val: apkCTS9,
Timeout: 5 * time.Minute,
}, {
Name: "vm",
ExtraAttr: []string{"group:mainline", "informational"},
ExtraSoftwareDeps: []string{"android_vm"},
ExtraData: []string{apkCTS11},
Val: apkCTS11,
Timeout: 5 * time.Minute,
}},
})
}
func runSingleDEQPTest(ctx context.Context, a *arc.ARC, tconn *chrome.TestConn, name string) error {
fileName := filepath.Join("/sdcard/testcases", name)
testing.ContextLog(ctx, "Starting dEQP app for ", fileName)
act, err := arc.NewActivity(a, packageName, activityName)
if err != nil {
return errors.Wrapf(err, "failed to create new activity for %v", name)
}
defer act.Close()
prefixArgs := []string{"-W", "-S", "-n"}
suffixArgs := []string{"-e", "cmdLine", "deqp --deqp-log-filename=/sdcard/" + name + "-results.qpa --deqp-caselist-file=" + fileName}
if err := act.StartWithArgs(ctx, tconn, prefixArgs, suffixArgs); err != nil {
return errors.Wrapf(err, "failed to start activity for %v", name)
}
return act.WaitForFinished(ctx, ctxutil.MaxTimeout)
}
// pushCaseListFilesToAndroid pushes each individual caselist file to the Android device via adb push.
func pushCaseListFilesToAndroid(ctx context.Context, s *testing.State, a *arc.ARC) error {
for _, testFile := range deqpTests {
testName := filepath.Base(testFile)
if err := a.PushFile(ctx, s.DataPath(testFile), filepath.Join("/sdcard/testcases", testName)); err != nil {
return err
}
}
return nil
}
// DEQP is the main tast test entry point.
// TODO(morg): Add support for different DEQP suite versions as a parameterized test flag (9.0r9, 9.0r10, etc).
func DEQP(ctx context.Context, s *testing.State) {
a := s.FixtValue().(*arc.PreData).ARC
cr := s.FixtValue().(*arc.PreData).Chrome
apk := s.Param().(string)
tconn, err := cr.TestAPIConn(ctx)
if err != nil {
s.Fatal("Failed to create Test API connection: ", err)
}
s.Log("Installing dEQP APK")
if err := a.Install(ctx, s.DataPath(apk), adb.InstallOptionGrantPermissions); err != nil {
s.Fatal("Failed installing app: ", err)
}
// Grant apk permissions to manage external storage. This is required
// for Android 11 devices.
if apk == apkCTS11 {
cmd := a.Command(ctx, "appops", "set", packageName, "MANAGE_EXTERNAL_STORAGE", "allow")
if err := cmd.Run(testexec.DumpLogOnError); err != nil {
s.Fatal("Failed to set app permissions: ", err)
}
}
// We need to push each file to the device because on ARCVM we do not expose the guest
// filesystem to the host.
s.Log("Pushing testcase files to Android device")
if err := pushCaseListFilesToAndroid(ctx, s, a); err != nil {
s.Fatal("Failed pushing testcase lists to Android: ", err)
}
failedTests := false
totalTests := 0
totalFailedTests := 0
for _, testFile := range deqpTests {
testName := filepath.Base(testFile)
c, err := ioutil.ReadFile(s.DataPath(testFile))
if err != nil {
s.Fatalf("Failed to read %s: %v", testFile, err)
}
if len(strings.TrimSpace(string(c))) == 0 {
s.Logf("Testcase %v is empty. Skipping", testFile)
continue
}
if err := runSingleDEQPTest(ctx, a, tconn, testName); err != nil {
s.Fatalf("Test %s has failed: %v ", testName, err)
}
logFileOnGuest := filepath.Join("/sdcard", testName+"-results.qpa")
logFileOnHost := filepath.Join(s.OutDir(), testName+"-results.qpa")
if err := a.PullFile(ctx, logFileOnGuest, logFileOnHost); err != nil {
s.Fatalf("Failed to obtain test results for %s: %v", testName, err)
}
stats, nonFailed, err := graphics.ParseDEQPOutput(logFileOnHost)
if err != nil {
s.Logf("Failed to parse results for %s: %v", logFileOnHost, err)
s.Logf("The test for %s will be considered failed", testName)
failedTests = true
}
testsRun := 0
s.Logf("Parsing %v results", testName)
for r, c := range stats {
if c > 0 && graphics.DEQPOutcomeIsFailure(r) {
failedTests = true
}
testsRun += int(c)
}
s.Logf("%v: %v", testName, stats)
s.Logf("Tests run: %v. Tests considered failed: %v", testsRun, testsRun-len(nonFailed))
totalTests += testsRun
totalFailedTests += testsRun - len(nonFailed)
}
if failedTests {
s.Fatalf("Some dEQP tests did not pass: %v(failed)/%v(total)", totalFailedTests, totalTests)
}
}