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:
Stephan Altmueller 2018-01-08 15:53:37 -05:00 committed by Skia Commit-Bot
parent c0034179a1
commit c35959f3cb
15 changed files with 566 additions and 66 deletions

12
infra/cts/README.md Normal file
View 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
View 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:]...)
}

View File

@ -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"

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,4 @@
<resources>
<string name="app_name">SkQP App</string>
<string name="title_activity_skqp">SkQP Activity</string>
</resources>

View File

@ -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>

View File

@ -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);