First version of SkQP app to run on Firebase Testlab
Adds activities to the skqp app so it can run as an Android app (as opposed to just instrumentation tests). A user can trigger the tests via a button. Adds the an intent receiver so the tests can be triggered on Firebase Testlab via the gameloop option. It adds the run_testlab.go script to run an apk across devices on Firebase Testlab. Bug: skia: Change-Id: I3ff5c37d743fa47913a916a0fa1e7db3c2cc79c7 Reviewed-on: https://skia-review.googlesource.com/89163 Commit-Queue: Stephan Altmueller <stephana@google.com> Reviewed-by: Kevin Lubick <kjlubick@google.com> Reviewed-by: Derek Sollenberger <djsollen@google.com>
This commit is contained in:
parent
c0034179a1
commit
c35959f3cb
12
infra/cts/README.md
Normal file
12
infra/cts/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
Running CTS on Firebase Testlab
|
||||
===============================
|
||||
|
||||
The run_testlab.go script uploads a given apk to Firebase Testlab and
|
||||
runs them on the list of devices whitelisted in the script.
|
||||
See the WHITELIST\_DEV\_IDS variable.
|
||||
|
||||
To run 'skqpapp.apk' on Testlab run the following command:
|
||||
|
||||
```
|
||||
$ go run run_testlab.go --logtostderr --service_account_file=service-account.json skqpapp.apk
|
||||
```
|
245
infra/cts/run_testlab.go
Normal file
245
infra/cts/run_testlab.go
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright 2018 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
gstorage "google.golang.org/api/storage/v1"
|
||||
|
||||
"go.skia.org/infra/go/auth"
|
||||
"go.skia.org/infra/go/common"
|
||||
"go.skia.org/infra/go/sklog"
|
||||
"go.skia.org/infra/go/util"
|
||||
"go.skia.org/infra/golden/go/tsuite"
|
||||
)
|
||||
|
||||
// TODO(stephana): Convert the hard coded whitelist to a command line flag that
|
||||
// loads a file with the whitelisted devices and versions. Make sure to include
|
||||
// human readable names for the devices.
|
||||
|
||||
var (
|
||||
// WHITELIST_DEV_IDS contains a mapping from the device id to the list of
|
||||
// Android API versions that we should run agains. Usually this will be the
|
||||
// latest version. To see available devices and version run with
|
||||
// --dryrun flag or run '$ gcloud firebase test android models list'
|
||||
|
||||
WHITELIST_DEV_IDS = map[string][]string{
|
||||
"A0001": {"22"},
|
||||
// "E5803": {"22"}, deprecated
|
||||
// "F5121": {"23"}, deprecated
|
||||
"G8142": {"25"},
|
||||
"HWMHA": {"24"},
|
||||
"SH-04H": {"23"},
|
||||
"athene": {"23"},
|
||||
"athene_f": {"23"},
|
||||
"hammerhead": {"23"},
|
||||
"harpia": {"23"},
|
||||
"hero2lte": {"23"},
|
||||
"herolte": {"24v"},
|
||||
"j1acevelte": {"22"},
|
||||
"j5lte": {"23"},
|
||||
"j7xelte": {"23"},
|
||||
"lucye": {"24"},
|
||||
"mako": {"22"},
|
||||
"osprey_umts": {"22"},
|
||||
"p1": {"22"},
|
||||
"sailfish": {"26"},
|
||||
"shamu": {"23"},
|
||||
"trelte": {"22"},
|
||||
"zeroflte": {"22"},
|
||||
"zerolte": {"22"},
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
META_DATA_FILENAME = "meta.json"
|
||||
)
|
||||
|
||||
// Command line flags.
|
||||
var (
|
||||
serviceAccountFile = flag.String("service_account_file", "", "Credentials file for service account.")
|
||||
dryRun = flag.Bool("dryrun", false, "Print out the command and quit without triggering tests.")
|
||||
)
|
||||
|
||||
const (
|
||||
RUN_TESTS_TEMPLATE = `gcloud beta firebase test android run
|
||||
--type=game-loop
|
||||
--app=%s
|
||||
--results-bucket=%s
|
||||
--results-dir=%s
|
||||
--directories-to-pull=/sdcard/Android/data/org.skia.skqpapp
|
||||
%s
|
||||
`
|
||||
MODEL_VERSION_TMPL = "--device model=%s,version=%s,orientation=portrait"
|
||||
RESULT_BUCKET = "skia-firebase-test-lab"
|
||||
RESULT_DIR_TMPL = "testruns/%s/%s"
|
||||
RUN_ID_TMPL = "testrun-%d"
|
||||
CMD_AVAILABE_DEVICES = "gcloud firebase test android models list --format json"
|
||||
)
|
||||
|
||||
func main() {
|
||||
common.Init()
|
||||
|
||||
// Get the apk.
|
||||
args := flag.Args()
|
||||
apk_path := args[0]
|
||||
|
||||
// Make sure we can get the service account client.
|
||||
client, err := auth.NewJWTServiceAccountClient("", *serviceAccountFile, nil, gstorage.CloudPlatformScope, "https://www.googleapis.com/auth/userinfo.email")
|
||||
if err != nil {
|
||||
sklog.Fatalf("Failed to authenticate service account: %s. Run 'get_service_account' to obtain a service account file.", err)
|
||||
}
|
||||
|
||||
// Get list of all available devices.
|
||||
devices, ignoredDevices, err := getAvailableDevices(WHITELIST_DEV_IDS)
|
||||
if err != nil {
|
||||
sklog.Fatalf("Unable to retrieve available devices: %s", err)
|
||||
}
|
||||
sklog.Infof("---")
|
||||
sklog.Infof("Selected devices:")
|
||||
logDevices(devices)
|
||||
|
||||
if err := runTests(apk_path, devices, ignoredDevices, client, *dryRun); err != nil {
|
||||
sklog.Fatalf("Error triggering tests on Firebase: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// getAvailableDevices is given a whitelist. It queries Firebase Testlab for all
|
||||
// available devices and then returns a list of devices to be tested and the list
|
||||
// of ignored devices.
|
||||
func getAvailableDevices(whiteList map[string][]string) ([]*tsuite.DeviceVersions, []*tsuite.DeviceVersions, error) {
|
||||
// Get the list of all devices in JSON format from Firebase testlab.
|
||||
var buf bytes.Buffer
|
||||
cmd := parseCommand(CMD_AVAILABE_DEVICES)
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = os.Stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Unmarshal the result.
|
||||
foundDevices := []*tsuite.FirebaseDevice{}
|
||||
bufBytes := buf.Bytes()
|
||||
if err := json.Unmarshal(bufBytes, &foundDevices); err != nil {
|
||||
return nil, nil, sklog.FmtErrorf("Unmarshal of device information failed: %s \nJSON Input: %s\n", err, string(bufBytes))
|
||||
}
|
||||
|
||||
// iterate over the available devices and partition them.
|
||||
allDevices := make([]*tsuite.DeviceVersions, 0, len(foundDevices))
|
||||
ret := make([]*tsuite.DeviceVersions, 0, len(foundDevices))
|
||||
ignored := make([]*tsuite.DeviceVersions, 0, len(foundDevices))
|
||||
for _, dev := range foundDevices {
|
||||
// Filter out all the virtual devices.
|
||||
if dev.Form == "PHYSICAL" {
|
||||
// Only include devices that are on the whitelist and have versions defined.
|
||||
if foundVersions, ok := whiteList[dev.ID]; ok && (len(foundVersions) > 0) {
|
||||
versionSet := util.NewStringSet(dev.VersionIDs)
|
||||
reqVersions := util.NewStringSet(foundVersions)
|
||||
whiteListVersions := versionSet.Intersect(reqVersions).Keys()
|
||||
ignoredVersions := versionSet.Complement(reqVersions).Keys()
|
||||
sort.Strings(whiteListVersions)
|
||||
sort.Strings(ignoredVersions)
|
||||
ret = append(ret, &tsuite.DeviceVersions{Device: dev, Versions: whiteListVersions})
|
||||
ignored = append(ignored, &tsuite.DeviceVersions{Device: dev, Versions: ignoredVersions})
|
||||
} else {
|
||||
ignored = append(ignored, &tsuite.DeviceVersions{Device: dev, Versions: dev.VersionIDs})
|
||||
}
|
||||
allDevices = append(allDevices, &tsuite.DeviceVersions{Device: dev, Versions: dev.VersionIDs})
|
||||
}
|
||||
}
|
||||
|
||||
sklog.Infof("All devices:")
|
||||
logDevices(allDevices)
|
||||
|
||||
return ret, ignored, nil
|
||||
}
|
||||
|
||||
// runTests runs the given apk on the given list of devices.
|
||||
func runTests(apk_path string, devices, ignoredDevices []*tsuite.DeviceVersions, client *http.Client, dryRun bool) error {
|
||||
// Get the model-version we want to test. Assume on average each model has 5 supported versions.
|
||||
modelSelectors := make([]string, 0, len(devices)*5)
|
||||
for _, devRec := range devices {
|
||||
for _, version := range devRec.Versions {
|
||||
modelSelectors = append(modelSelectors, fmt.Sprintf(MODEL_VERSION_TMPL, devRec.Device.ID, version))
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
nowMs := now.UnixNano() / int64(time.Millisecond)
|
||||
runID := fmt.Sprintf(RUN_ID_TMPL, nowMs)
|
||||
resultsDir := fmt.Sprintf(RESULT_DIR_TMPL, now.Format("2006/01/02/15"), runID)
|
||||
cmdStr := fmt.Sprintf(RUN_TESTS_TEMPLATE, apk_path, RESULT_BUCKET, resultsDir, strings.Join(modelSelectors, "\n"))
|
||||
cmdStr = strings.TrimSpace(strings.Replace(cmdStr, "\n", " ", -1))
|
||||
|
||||
// Run the command.
|
||||
cmd := parseCommand(cmdStr)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stdout
|
||||
exitCode := 0
|
||||
|
||||
if dryRun {
|
||||
fmt.Printf("[dry run]: Would have run this command: %s\n", cmdStr)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
// Get the exit code.
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
ws := exitError.Sys().(syscall.WaitStatus)
|
||||
exitCode = ws.ExitStatus()
|
||||
}
|
||||
sklog.Errorf("Error running tests: %s", err)
|
||||
sklog.Errorf("Exit code: %d", exitCode)
|
||||
|
||||
// Exit code 10 means triggering on Testlab succeeded, but but some of the
|
||||
// runs on devices failed. We consider it a success for this script.
|
||||
if exitCode != 10 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Store the result in a meta json file.
|
||||
meta := &tsuite.TestRunMeta{
|
||||
ID: runID,
|
||||
TS: nowMs,
|
||||
Devices: devices,
|
||||
IgnoredDevices: ignoredDevices,
|
||||
ExitCode: exitCode,
|
||||
}
|
||||
|
||||
meta.WriteToGCS(RESULT_BUCKET, resultsDir+"/"+META_DATA_FILENAME, client)
|
||||
return nil
|
||||
}
|
||||
|
||||
// logDevices logs the given list of devices.
|
||||
func logDevices(devices []*tsuite.DeviceVersions) {
|
||||
sklog.Infof("Found %d devices.", len(devices))
|
||||
for _, dev := range devices {
|
||||
sklog.Infof("%-15s %-30s %v / %v", dev.Device.ID, dev.Device.Name, dev.Device.VersionIDs, dev.Versions)
|
||||
}
|
||||
}
|
||||
|
||||
// parseCommad parses a command line and wraps it in an exec.Command instance.
|
||||
func parseCommand(cmdStr string) *exec.Cmd {
|
||||
cmdArgs := strings.Split(strings.TrimSpace(cmdStr), " ")
|
||||
for idx := range cmdArgs {
|
||||
cmdArgs[idx] = strings.TrimSpace(cmdArgs[idx])
|
||||
}
|
||||
return exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
||||
}
|
@ -7,13 +7,13 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-annotations:24.0.0'
|
||||
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
compile 'com.android.support:design:26.+'
|
||||
compile 'com.android.support.test:runner:0.5'
|
||||
compile group: 'junit', name: 'junit', version: '4.+'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "22.0.1"
|
||||
defaultConfig {
|
||||
applicationId "org.skia.skqp"
|
||||
|
@ -3,7 +3,30 @@
|
||||
package="org.skia.skqp"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
<application><uses-library android:name="android.test.runner" /></application>
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:theme="@style/AppTheme"
|
||||
android:label="SkQP"
|
||||
android:debuggable="true">
|
||||
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SkQPActivity"
|
||||
android:label="@string/title_activity_skqp"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.intent.action.TEST_LOOP"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="application/javascript"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="org.skia.skqp"></instrumentation>
|
||||
</manifest>
|
||||
|
@ -0,0 +1,21 @@
|
||||
package org.skia.skqp;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
}
|
||||
|
||||
public void startTests(View view) {
|
||||
Intent intent = new Intent(this, SkQPActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
@ -7,8 +7,90 @@
|
||||
|
||||
package org.skia.skqp;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.util.Log;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
@RunWith(SkQPRunner.class)
|
||||
public class SkQP {}
|
||||
public class SkQP {
|
||||
protected native void nInit(AssetManager assetManager, String dataDir);
|
||||
protected native float nExecuteGM(int gm, int backend) throws SkQPException;
|
||||
protected native String[] nExecuteUnitTest(int test);
|
||||
protected native void nMakeReport();
|
||||
|
||||
protected String[] mGMs;
|
||||
protected String[] mBackends;
|
||||
protected String[] mUnitTests;
|
||||
|
||||
protected static final String kSkiaGM = "SkiaGM_";
|
||||
protected static final String kSkiaUnitTests = "Skia_UnitTests";
|
||||
protected static final String LOG_PREFIX = "org.skis.skqp";
|
||||
|
||||
static {
|
||||
System.loadLibrary("skqp_app");
|
||||
}
|
||||
|
||||
protected void runTests(Context context, String outputDirPath) {
|
||||
Log.w(LOG_PREFIX, "Output Dir: " + outputDirPath);
|
||||
File outputDir = new File(outputDirPath);
|
||||
if (outputDir.exists()) {
|
||||
try {
|
||||
deleteDirectoryContents(outputDir);
|
||||
} catch (IOException e) {
|
||||
Log.w(LOG_PREFIX, "DeleteDirectoryContents: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Note: nInit will initialize the mGMs, mBackends and mUnitTests fields.
|
||||
AssetManager assetManager = context.getResources().getAssets();
|
||||
this.nInit(assetManager, outputDirPath);
|
||||
|
||||
for (int backend = 0; backend < mBackends.length; backend++) {
|
||||
String classname = kSkiaGM + mBackends[backend];
|
||||
for (int gm = 0; gm < mGMs.length; gm++) {
|
||||
String testName = kSkiaGM + mBackends[backend] + "_" +mGMs[gm];
|
||||
float value = java.lang.Float.MAX_VALUE;
|
||||
String error = null;
|
||||
Log.w(LOG_PREFIX, "Running: " + testName);
|
||||
try {
|
||||
value = this.nExecuteGM(gm, backend);
|
||||
} catch (SkQPException exept) {
|
||||
error = exept.getMessage();
|
||||
}
|
||||
if (error != null) {
|
||||
// Record error message and carry on.
|
||||
} else if (value != 0) {
|
||||
// Record failure and carry on.
|
||||
// SkQPRunner.Fail(desc, notifier, String.format(
|
||||
// "Image mismatch: max channel diff = %f", value));
|
||||
} else {
|
||||
// Record success for this test.
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int unitTest = 0; unitTest < mUnitTests.length; unitTest++) {
|
||||
String testName = kSkiaUnitTests + "_" + mUnitTests[unitTest];
|
||||
Log.w(LOG_PREFIX, "Running: " + testName);
|
||||
String[] errors = this.nExecuteUnitTest(unitTest);
|
||||
if (errors != null && errors.length > 0) {
|
||||
for (String error : errors) {
|
||||
// Record unit test failures.
|
||||
}
|
||||
} else {
|
||||
// Record success.
|
||||
}
|
||||
}
|
||||
nMakeReport();
|
||||
}
|
||||
|
||||
protected static void deleteDirectoryContents(File f) throws IOException {
|
||||
for (File s : f.listFiles()) {
|
||||
if (s.isDirectory()) {
|
||||
deleteDirectoryContents(s);
|
||||
}
|
||||
s.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,35 @@
|
||||
package org.skia.skqp;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
|
||||
public class SkQPActivity extends AppCompatActivity implements Runnable {
|
||||
private SkQP testRunner = new SkQP();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_skqp);
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
// Start the tests.
|
||||
run();
|
||||
}
|
||||
|
||||
// run implements the Runnable interface.
|
||||
public void run() {
|
||||
// Note: /sdcard/Android/data/<package-name> is a location an app is allowed to write to.
|
||||
// When running tests on Firebase it expects any result files to have a '/sdcard
|
||||
// prefix or it won't trigger tests from the CLI.
|
||||
|
||||
Context context = getApplicationContext();
|
||||
String outputDirPath = "/sdcard/Android/data/" + context.getPackageName();
|
||||
testRunner.runTests(context, outputDirPath);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
@ -21,30 +21,8 @@ import org.junit.runner.notification.Failure;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
|
||||
public class SkQPRunner extends Runner {
|
||||
private native void nInit(AssetManager assetManager, String dataDir);
|
||||
private native float nExecuteGM(int gm, int backend) throws SkQPException;
|
||||
private native String[] nExecuteUnitTest(int test);
|
||||
private native void nMakeReport();
|
||||
|
||||
private AssetManager mAssetManager;
|
||||
private String[] mGMs;
|
||||
private String[] mBackends;
|
||||
private String[] mUnitTests;
|
||||
|
||||
private static boolean sOnceFlag = false;
|
||||
private static final String kSkiaGM = "SkiaGM_";
|
||||
private static final String kSkiaUnitTests = "Skia_UnitTests";
|
||||
|
||||
private Description mDescription;
|
||||
|
||||
private static void DeleteDirectoryContents(File f) throws IOException {
|
||||
for (File s : f.listFiles()) {
|
||||
if (s.isDirectory()) {
|
||||
SkQPRunner.DeleteDirectoryContents(s);
|
||||
}
|
||||
s.delete();
|
||||
}
|
||||
}
|
||||
private SkQP impl;
|
||||
|
||||
private static void Fail(Description desc, RunNotifier notifier, String failure) {
|
||||
notifier.fireTestFailure(new Failure(desc, new Throwable(failure)));
|
||||
@ -53,37 +31,30 @@ public class SkQPRunner extends Runner {
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public SkQPRunner(Class testClass) {
|
||||
synchronized (SkQPRunner.class) {
|
||||
if (sOnceFlag) {
|
||||
throw new IllegalStateException("Error multiple SkQPs defined");
|
||||
}
|
||||
sOnceFlag = true;
|
||||
}
|
||||
System.loadLibrary("skqp_app");
|
||||
|
||||
impl = new SkQP();
|
||||
Context context = InstrumentationRegistry.getTargetContext();
|
||||
File filesDir = context.getFilesDir();
|
||||
try {
|
||||
SkQPRunner.DeleteDirectoryContents(filesDir);
|
||||
SkQP.deleteDirectoryContents(filesDir);
|
||||
} catch (IOException e) {
|
||||
Log.w("org.skis.skqp", "DeleteDirectoryContents: " + e.getMessage());
|
||||
}
|
||||
|
||||
Resources resources = context.getResources();
|
||||
mAssetManager = resources.getAssets();
|
||||
this.nInit(mAssetManager, filesDir.getAbsolutePath());
|
||||
AssetManager mAssetManager = resources.getAssets();
|
||||
impl.nInit(mAssetManager, filesDir.getAbsolutePath());
|
||||
|
||||
mDescription = Description.createSuiteDescription(testClass);
|
||||
Annotation annots[] = new Annotation[0];
|
||||
for (int backend = 0; backend < mBackends.length; backend++) {
|
||||
String classname = kSkiaGM + mBackends[backend];
|
||||
for (int gm = 0; gm < mGMs.length; gm++) {
|
||||
mDescription.addChild(Description.createTestDescription(classname, mGMs[gm], annots));
|
||||
for (int backend = 0; backend < impl.mBackends.length; backend++) {
|
||||
String classname = SkQP.kSkiaGM + impl.mBackends[backend];
|
||||
for (int gm = 0; gm < impl.mGMs.length; gm++) {
|
||||
mDescription.addChild(Description.createTestDescription(classname, impl.mGMs[gm], annots));
|
||||
}
|
||||
}
|
||||
for (int unitTest = 0; unitTest < mUnitTests.length; unitTest++) {
|
||||
mDescription.addChild(Description.createTestDescription(kSkiaUnitTests,
|
||||
mUnitTests[unitTest], annots));
|
||||
for (int unitTest = 0; unitTest < impl.mUnitTests.length; unitTest++) {
|
||||
mDescription.addChild(Description.createTestDescription(SkQP.kSkiaUnitTests,
|
||||
impl.mUnitTests[unitTest], annots));
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,20 +62,20 @@ public class SkQPRunner extends Runner {
|
||||
public Description getDescription() { return mDescription; }
|
||||
|
||||
@Override
|
||||
public int testCount() { return mUnitTests.length + mGMs.length * mBackends.length; }
|
||||
public int testCount() { return impl.mUnitTests.length + impl.mGMs.length * impl.mBackends.length; }
|
||||
|
||||
@Override
|
||||
public void run(RunNotifier notifier) {
|
||||
Annotation annots[] = new Annotation[0];
|
||||
for (int backend = 0; backend < mBackends.length; backend++) {
|
||||
String classname = kSkiaGM + mBackends[backend];
|
||||
for (int gm = 0; gm < mGMs.length; gm++) {
|
||||
Description desc = Description.createTestDescription(classname, mGMs[gm], annots);
|
||||
for (int backend = 0; backend < impl.mBackends.length; backend++) {
|
||||
String classname = SkQP.kSkiaGM + impl.mBackends[backend];
|
||||
for (int gm = 0; gm < impl.mGMs.length; gm++) {
|
||||
Description desc = Description.createTestDescription(classname, impl.mGMs[gm], annots);
|
||||
notifier.fireTestStarted(desc);
|
||||
float value = java.lang.Float.MAX_VALUE;
|
||||
String error = null;
|
||||
try {
|
||||
value = this.nExecuteGM(gm, backend);
|
||||
value = impl.nExecuteGM(gm, backend);
|
||||
} catch (SkQPException exept) {
|
||||
error = exept.getMessage();
|
||||
}
|
||||
@ -117,11 +88,11 @@ public class SkQPRunner extends Runner {
|
||||
notifier.fireTestFinished(desc);
|
||||
}
|
||||
}
|
||||
for (int unitTest = 0; unitTest < mUnitTests.length; unitTest++) {
|
||||
for (int unitTest = 0; unitTest < impl.mUnitTests.length; unitTest++) {
|
||||
Description desc = Description.createTestDescription(
|
||||
kSkiaUnitTests, mUnitTests[unitTest], annots);
|
||||
SkQP.kSkiaUnitTests, impl.mUnitTests[unitTest], annots);
|
||||
notifier.fireTestStarted(desc);
|
||||
String[] errors = this.nExecuteUnitTest(unitTest);
|
||||
String[] errors = impl.nExecuteUnitTest(unitTest);
|
||||
if (errors != null && errors.length > 0) {
|
||||
for (String error : errors) {
|
||||
SkQPRunner.Fail(desc, notifier, error);
|
||||
@ -129,7 +100,7 @@ public class SkQPRunner extends Runner {
|
||||
}
|
||||
notifier.fireTestFinished(desc);
|
||||
}
|
||||
this.nMakeReport();
|
||||
impl.nMakeReport();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="org.skia.skqp.MainActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="368dp"
|
||||
android:layout_height="495dp"
|
||||
android:orientation="vertical"
|
||||
tools:layout_editor_absoluteX="8dp"
|
||||
tools:layout_editor_absoluteY="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Nothing to see here !"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_start"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:keyboardNavigationCluster="false"
|
||||
android:onClick="startTests"
|
||||
android:text="Start Tests !" />
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="org.skia.skqp.SkQPActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<include layout="@layout/content_skqp" />
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:context="org.skia.skqp.SkQPActivity"
|
||||
tools:showIn="@layout/activity_skqp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Running SkQP tests ..."
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="visible" />
|
||||
</android.support.constraint.ConstraintLayout>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
</resources>
|
@ -0,0 +1,4 @@
|
||||
<resources>
|
||||
<string name="app_name">SkQP App</string>
|
||||
<string name="title_activity_skqp">SkQP Activity</string>
|
||||
</resources>
|
@ -0,0 +1,20 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
</resources>
|
@ -19,11 +19,11 @@
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
extern "C" {
|
||||
JNIEXPORT void JNICALL Java_org_skia_skqp_SkQPRunner_nInit(JNIEnv*, jobject, jobject, jstring);
|
||||
JNIEXPORT jfloat JNICALL Java_org_skia_skqp_SkQPRunner_nExecuteGM(JNIEnv*, jobject, jint, jint);
|
||||
JNIEXPORT jobjectArray JNICALL Java_org_skia_skqp_SkQPRunner_nExecuteUnitTest(JNIEnv*, jobject,
|
||||
JNIEXPORT void JNICALL Java_org_skia_skqp_SkQP_nInit(JNIEnv*, jobject, jobject, jstring);
|
||||
JNIEXPORT jfloat JNICALL Java_org_skia_skqp_SkQP_nExecuteGM(JNIEnv*, jobject, jint, jint);
|
||||
JNIEXPORT jobjectArray JNICALL Java_org_skia_skqp_SkQP_nExecuteUnitTest(JNIEnv*, jobject,
|
||||
jint);
|
||||
JNIEXPORT void JNICALL Java_org_skia_skqp_SkQPRunner_nMakeReport(JNIEnv*, jobject);
|
||||
JNIEXPORT void JNICALL Java_org_skia_skqp_SkQP_nMakeReport(JNIEnv*, jobject);
|
||||
} // extern "C"
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -118,7 +118,7 @@ jobjectArray to_java_string_array(JNIEnv* env,
|
||||
return jarray;
|
||||
}
|
||||
|
||||
void Java_org_skia_skqp_SkQPRunner_nInit(JNIEnv* env, jobject object, jobject assetManager,
|
||||
void Java_org_skia_skqp_SkQP_nInit(JNIEnv* env, jobject object, jobject assetManager,
|
||||
jstring dataDir) {
|
||||
jclass clazz = env->GetObjectClass(object);
|
||||
jassert(env, assetManager);
|
||||
@ -147,7 +147,7 @@ void Java_org_skia_skqp_SkQPRunner_nInit(JNIEnv* env, jobject object, jobject as
|
||||
to_java_string_array(env, gGMs, gm_runner::GetGMName));
|
||||
}
|
||||
|
||||
jfloat Java_org_skia_skqp_SkQPRunner_nExecuteGM(JNIEnv* env,
|
||||
jfloat Java_org_skia_skqp_SkQP_nExecuteGM(JNIEnv* env,
|
||||
jobject object,
|
||||
jint gmIndex,
|
||||
jint backendIndex) {
|
||||
@ -173,7 +173,7 @@ jfloat Java_org_skia_skqp_SkQPRunner_nExecuteGM(JNIEnv* env,
|
||||
return result;
|
||||
}
|
||||
|
||||
jobjectArray Java_org_skia_skqp_SkQPRunner_nExecuteUnitTest(JNIEnv* env,
|
||||
jobjectArray Java_org_skia_skqp_SkQP_nExecuteUnitTest(JNIEnv* env,
|
||||
jobject object,
|
||||
jint index) {
|
||||
jassert(env, index < (jint)gUnitTests.size());
|
||||
@ -193,7 +193,7 @@ jobjectArray Java_org_skia_skqp_SkQPRunner_nExecuteUnitTest(JNIEnv* env,
|
||||
return (jobjectArray)env->NewGlobalRef(array);
|
||||
}
|
||||
|
||||
void Java_org_skia_skqp_SkQPRunner_nMakeReport(JNIEnv*, jobject) {
|
||||
void Java_org_skia_skqp_SkQP_nMakeReport(JNIEnv*, jobject) {
|
||||
std::string reportDirectoryPath;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(gMutex);
|
||||
|
Loading…
Reference in New Issue
Block a user