diff options
author | Felipe Leme <felipeal@google.com> | 2018-04-30 09:39:55 -0700 |
---|---|---|
committer | Felipe Leme <felipeal@google.com> | 2018-05-01 12:40:33 -0700 |
commit | 6378b48f239963cfad496f9c7ffec3b99d1a1fd4 (patch) | |
tree | 67f584199cdea591f09a140a04653323ad2ddad9 /apct-tests/perftests/utils | |
parent | 633c4d9875c019539d6e6f0821977b8d586e64e8 (diff) |
Initial PERF tests for autofill.
These tests render an activity that has 2 autofillable views (username and
password) and keep focusing between then, observing what happens in 4 scenarios:
1. No autofill service (baseline)
2. Autofill service returning no datasets.
3. Autofill service returning a dataset with username and password.
4. Autofill service returning a dataset with just username.
Because this change introduced a helper class to run shell commands without
needing the UiAutomator package, it also changed the MultiUserPerfTests to use
such helper.
Test: mmma -j ./frameworks/base/apct-tests/perftests/core/ && \
adb install -r $OUT/data/app/CorePerfTests/CorePerfTests.apk && \
adb shell am instrument -w -e class android.view.autofill.AutofillPerfTest \
com.android.perftests.core/android.support.test.runner.AndroidJUnitRunner
Test: mmma -j ./frameworks/base/apct-tests/perftests/multiuser && \
adb install -r $OUT/data/app/MultiUserPerfTests/MultiUserPerfTests.apk && \
adb shell am instrument -w -e class android.multiuser.UserLifecycleTests \
com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner
Bug: 38345816
Change-Id: Ie283dff8dd19c38ea829de9164b23aae2bfeb015
Diffstat (limited to 'apct-tests/perftests/utils')
4 files changed, 267 insertions, 1 deletions
diff --git a/apct-tests/perftests/utils/Android.mk b/apct-tests/perftests/utils/Android.mk index 55c13b087626..604f0adbe23e 100644 --- a/apct-tests/perftests/utils/Android.mk +++ b/apct-tests/perftests/utils/Android.mk @@ -1,7 +1,9 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_STATIC_JAVA_LIBRARIES := android-support-test +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-test \ + androidx.annotation_annotation # Build all java files in the java subdirectory LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/OneTimeSettingsListener.java b/apct-tests/perftests/utils/src/android/perftests/utils/OneTimeSettingsListener.java new file mode 100644 index 000000000000..37af4c7f6181 --- /dev/null +++ b/apct-tests/perftests/utils/src/android/perftests/utils/OneTimeSettingsListener.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.perftests.utils; + +import static android.perftests.utils.SettingsHelper.NAMESPACE_SECURE; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; + +import androidx.annotation.NonNull; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Helper used to block tests until a secure settings value has been updated. + */ +public final class OneTimeSettingsListener extends ContentObserver { + private final CountDownLatch mLatch = new CountDownLatch(1); + private final ContentResolver mResolver; + private final String mKey; + private final int mTimeoutMs; + + public OneTimeSettingsListener(@NonNull Context context, @NonNull String namespace, + @NonNull String key, int timeoutMs) { + super(new Handler(Looper.getMainLooper())); + mKey = key; + mResolver = context.getContentResolver(); + mTimeoutMs = timeoutMs; + final Uri uri; + switch (namespace) { + case NAMESPACE_SECURE: + uri = Settings.Secure.getUriFor(key); + break; + default: + throw new IllegalArgumentException("invalid namespace: " + namespace); + } + mResolver.registerContentObserver(uri, false, this); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + mResolver.unregisterContentObserver(this); + mLatch.countDown(); + } + + /** + * Blocks for a few seconds until it's called, or throws an {@link IllegalStateException} if + * it isn't. + */ + public void assertCalled() { + try { + final boolean updated = mLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS); + if (!updated) { + throw new IllegalStateException( + "Settings " + mKey + " not called in " + mTimeoutMs + "ms"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("Interrupted", e); + } + } +} diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/SettingsHelper.java b/apct-tests/perftests/utils/src/android/perftests/utils/SettingsHelper.java new file mode 100644 index 000000000000..d7d1d6b7f817 --- /dev/null +++ b/apct-tests/perftests/utils/src/android/perftests/utils/SettingsHelper.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.perftests.utils; + +import android.content.Context; +import android.provider.Settings; +import android.text.TextUtils; + +import java.util.Objects; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * Provides utilities to interact with the device's {@link Settings}. + */ +public final class SettingsHelper { + + public static final String NAMESPACE_SECURE = "secure"; + + private static int DEFAULT_TIMEOUT_MS = 5000; + + /** + * Uses a Shell command to "asynchronously" set the given preference, returning right away. + */ + public static void set(@NonNull String namespace, @NonNull String key, @Nullable String value) { + if (value == null) { + delete(namespace, key); + return; + } + ShellHelper.runShellCommand("settings put %s %s %s default", namespace, key, value); + } + + /** + * Uses a Shell command to "synchronously" set the given preference by registering a listener + * and wait until it's set. + */ + public static void syncSet(@NonNull Context context, @NonNull String namespace, + @NonNull String key, @Nullable String value) { + if (value == null) { + syncDelete(context, namespace, key); + return; + } + + String currentValue = get(namespace, key); + if (value.equals(currentValue)) { + // Already set, ignore + return; + } + + OneTimeSettingsListener observer = new OneTimeSettingsListener(context, namespace, key, + DEFAULT_TIMEOUT_MS); + set(namespace, key, value); + observer.assertCalled(); + assertNewValue(namespace, key, value); + } + + /** + * Uses a Shell command to "asynchronously" delete the given preference, returning right away. + */ + public static void delete(@NonNull String namespace, @NonNull String key) { + ShellHelper.runShellCommand("settings delete %s %s", namespace, key); + } + + /** + * Uses a Shell command to "synchronously" delete the given preference by registering a listener + * and wait until it's called. + */ + public static void syncDelete(@NonNull Context context, @NonNull String namespace, + @NonNull String key) { + String currentValue = get(namespace, key); + if (currentValue == null || currentValue.equals("null")) { + // Already set, ignore + return; + } + + OneTimeSettingsListener observer = new OneTimeSettingsListener(context, namespace, key, + DEFAULT_TIMEOUT_MS); + delete(namespace, key); + observer.assertCalled(); + assertNewValue(namespace, key, "null"); + } + + /** + * Gets the value of a given preference using Shell command. + */ + @NonNull + public static String get(@NonNull String namespace, @NonNull String key) { + return ShellHelper.runShellCommand("settings get %s %s", namespace, key); + } + + private static void assertNewValue(@NonNull String namespace, @NonNull String key, + @Nullable String expectedValue) { + String actualValue = get(namespace, key); + if (!Objects.equals(actualValue, expectedValue)) { + throw new AssertionError("invalid value for " + namespace + ":" + key + ": expected '" + + actualValue + "' , got '" + expectedValue + "'"); + } + } + + private SettingsHelper() { + throw new UnsupportedOperationException("contain static methods only"); + } +} diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java new file mode 100644 index 000000000000..cae87fb9c6e4 --- /dev/null +++ b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.perftests.utils; + +import android.app.UiAutomation; +import android.os.ParcelFileDescriptor; +import android.support.test.InstrumentationRegistry; +import android.text.TextUtils; +import android.util.AndroidRuntimeException; +import android.util.Log; + +import java.io.FileInputStream; + +import androidx.annotation.NonNull; + +/** + * Provides Shell-based utilities such as running a command. + */ +public final class ShellHelper { + + /** + * Runs a Shell command, returning a trimmed response. + */ + @NonNull + public static String runShellCommand(@NonNull String template, Object...args) { + String command = String.format(template, args); + UiAutomation automan = InstrumentationRegistry.getInstrumentation() + .getUiAutomation(); + ParcelFileDescriptor pfd = automan.executeShellCommand(command); + byte[] buf = new byte[512]; + int bytesRead; + try(FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { + StringBuilder stdout = new StringBuilder(); + while ((bytesRead = fis.read(buf)) != -1) { + stdout.append(new String(buf, 0, bytesRead)); + } + String result = stdout.toString(); + return TextUtils.isEmpty(result) ? "" : result.trim(); + } catch (Exception e) { + throw new AndroidRuntimeException("Command '" + command + "' failed: ", e); + } finally { + // Must disconnect UI automation after every call, otherwise its accessibility service + // skews the performance tests. + automan.destroy(); + } + } + + private ShellHelper() { + throw new UnsupportedOperationException("contain static methods only"); + } +} |