Fix and enable the fail Autofill Perf Tests.
1.Fix using the wrong AutofillId to set the dataset.
2.Fix using the old setValue API.
3.Unlock lockscreen before test.
4.Make sure the Settings value is set before the test.
Bug: 169368561
Bug: 162216576
Test: atest AutofillPerfTests
Change-Id: I2abf8db5271e8e1719f91514eadb22bf9b4f8bb5
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestHelper.java b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestHelper.java
new file mode 100644
index 0000000..0763729
--- /dev/null
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestHelper.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 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.view.autofill;
+
+import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.WindowNode;
+import android.service.autofill.FillContext;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Helper for common funcionalities.
+ */
+public class AutofillTestHelper {
+ private static final String TAG = "AutofillTestHelper";
+
+ /**
+ * Gets a node given its Android resource id, or {@code null} if not found.
+ */
+ public static ViewNode findNodeByResourceId(List<FillContext> contexts, String resourceId) {
+ for (FillContext context : contexts) {
+ ViewNode node = findNodeByResourceId(context.getStructure(), resourceId);
+ if (node != null) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets a node if it matches the filter criteria for the given id.
+ */
+ private static ViewNode findNodeByResourceId(AssistStructure structure, String id) {
+ Log.v(TAG, "Parsing request for activity " + structure.getActivityComponent());
+ final int nodes = structure.getWindowNodeCount();
+ for (int i = 0; i < nodes; i++) {
+ final WindowNode windowNode = structure.getWindowNodeAt(i);
+ final ViewNode rootNode = windowNode.getRootViewNode();
+ final ViewNode node = findNodeByResourceId(rootNode, id);
+ if (node != null) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets a node if it matches the filter criteria for the given id.
+ */
+ private static ViewNode findNodeByResourceId(ViewNode node, String id) {
+ if (id.equals(node.getIdEntry())) {
+ return node;
+ }
+ final int childrenSize = node.getChildCount();
+ if (childrenSize > 0) {
+ for (int i = 0; i < childrenSize; i++) {
+ final ViewNode found = findNodeByResourceId(node.getChildAt(i), id);
+ if (found != null) {
+ return found;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
index 2475d98..f65067f 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
@@ -26,6 +26,8 @@
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.Timeout;
+
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
@@ -50,6 +52,7 @@
final String testName = description.getDisplayName();
Log.i(TAG, "Starting " + testName);
+ prepareDevice();
enableVerboseLog();
// Prepare the service before each test.
// Disable the current AutofillService.
@@ -81,10 +84,21 @@
* Uses the {@code settings} binary to set the autofill service.
*/
void setAutofillService() {
+ String serviceName = MyAutofillService.COMPONENT_NAME;
SettingsHelper.syncSet(InstrumentationRegistry.getTargetContext(),
SettingsHelper.NAMESPACE_SECURE,
Settings.Secure.AUTOFILL_SERVICE,
- MyAutofillService.COMPONENT_NAME);
+ serviceName);
+ // Waits until the service is actually enabled.
+ Timeout timeout = new Timeout("CONNECTION_TIMEOUT", GENERIC_TIMEOUT_MS, 2F,
+ GENERIC_TIMEOUT_MS);
+ try {
+ timeout.run("Enabling Autofill service", () -> {
+ return isAutofillServiceEnabled(serviceName) ? serviceName : null;
+ });
+ } catch (Exception e) {
+ throw new AssertionError("Enabling Autofill service failed.");
+ }
}
/**
@@ -96,6 +110,26 @@
Settings.Secure.AUTOFILL_SERVICE);
}
+ /**
+ * Checks whether the given service is set as the autofill service for the default user.
+ */
+ private boolean isAutofillServiceEnabled(String serviceName) {
+ String actualName = SettingsHelper.get(SettingsHelper.NAMESPACE_SECURE,
+ Settings.Secure.AUTOFILL_SERVICE);
+ return serviceName.equals(actualName);
+ }
+
+ private void prepareDevice() {
+ // Unlock screen.
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+ // Dismiss keyguard, in case it's set as "Swipe to unlock".
+ runShellCommand("wm dismiss-keyguard");
+
+ // Collapse notifications.
+ runShellCommand("cmd statusbar collapse");
+ }
+
private void enableMyAutofillService() {
MyAutofillService.resetStaticState();
MyAutofillService.setEnabled(true);
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index 37b4bde..a5d1e00 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -28,12 +28,14 @@
import com.android.perftests.autofill.R;
-import org.junit.Ignore;
import org.junit.Test;
@LargeTest
public class LoginTest extends AbstractAutofillPerfTestCase {
+ public static final String ID_USERNAME = "username";
+ public static final String ID_PASSWORD = "password";
+
private EditText mUsername;
private EditText mPassword;
private AutofillManager mAfm;
@@ -78,6 +80,7 @@
// Must first focus in a field to trigger autofill and wait for service response
// outside the loop
mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+ mTestWatcher.waitServiceConnect();
MyAutofillService.getLastFillRequest();
// Then focus on password so loop start with focus away from username
mActivityRule.runOnUiThread(() -> mPassword.requestFocus());
@@ -94,13 +97,11 @@
/**
* Now the service returns autofill data, for both username and password.
*/
- // TODO(b/162216576): fix fail test and re-enable it
- @Ignore
@Test
public void testFocus_autofillBothFields() throws Throwable {
MyAutofillService.newCannedResponse()
- .setUsername(mUsername.getAutofillId(), "user")
- .setPassword(mPassword.getAutofillId(), "pass")
+ .setUsername(ID_USERNAME, "user")
+ .setPassword(ID_PASSWORD, "pass")
.reply();
mTestWatcher.setAutofillService();
@@ -112,6 +113,7 @@
// Must first trigger autofill and wait for service response outside the loop
mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+ mTestWatcher.waitServiceConnect();
MyAutofillService.getLastFillRequest();
callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
@@ -148,14 +150,12 @@
/**
* Now the service returns autofill data, but just for username.
*/
- // TODO(b/162216576): fix fail test and re-enable it
- @Ignore
@Test
public void testFocus_autofillUsernameOnly() throws Throwable {
// Must set ignored ids so focus on password does not trigger new requests
MyAutofillService.newCannedResponse()
- .setUsername(mUsername.getAutofillId(), "user")
- .setIgnored(mPassword.getAutofillId())
+ .setUsername(ID_USERNAME, "user")
+ .setIgnored(ID_PASSWORD)
.reply();
mTestWatcher.setAutofillService();
@@ -167,6 +167,7 @@
// Must first trigger autofill and wait for service response outside the loop
mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+ mTestWatcher.waitServiceConnect();
MyAutofillService.getLastFillRequest();
callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
@@ -224,8 +225,8 @@
@Test
public void testChange_autofillBothFields() throws Throwable {
MyAutofillService.newCannedResponse()
- .setUsername(mUsername.getAutofillId(), "user")
- .setPassword(mPassword.getAutofillId(), "pass")
+ .setUsername(ID_USERNAME, "user")
+ .setPassword(ID_PASSWORD, "pass")
.reply();
mTestWatcher.setAutofillService();
@@ -239,8 +240,8 @@
public void testChange_autofillUsernameOnly() throws Throwable {
// Must set ignored ids so focus on password does not trigger new requests
MyAutofillService.newCannedResponse()
- .setUsername(mUsername.getAutofillId(), "user")
- .setIgnored(mPassword.getAutofillId())
+ .setUsername(ID_USERNAME, "user")
+ .setIgnored(ID_PASSWORD)
.reply();
mTestWatcher.setAutofillService();
@@ -266,13 +267,11 @@
}
}
- // TODO(b/162216576): fix fail test and re-enable it
- @Ignore
@Test
public void testCallbacks() throws Throwable {
MyAutofillService.newCannedResponse()
- .setUsername(mUsername.getAutofillId(), "user")
- .setPassword(mPassword.getAutofillId(), "pass")
+ .setUsername(ID_USERNAME, "user")
+ .setPassword(ID_PASSWORD, "pass")
.reply();
mTestWatcher.setAutofillService();
@@ -282,6 +281,7 @@
// Must first focus in a field to trigger autofill and wait for service response
// outside the loop
mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+ mTestWatcher.waitServiceConnect();
MyAutofillService.getLastFillRequest();
callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java b/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
index ddac68b..5db6597 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
@@ -15,6 +15,7 @@
*/
package android.view.autofill;
+import android.app.assist.AssistStructure.ViewNode;
import android.os.CancellationSignal;
import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
@@ -126,25 +127,31 @@
onError("ignoring onFillRequest(): response not set", callback);
return;
}
- // TODO(b/162216576): fix error FillResponse
Dataset.Builder dataset = new Dataset.Builder(newDatasetPresentation("dataset"));
boolean hasData = false;
if (response.mUsername != null) {
hasData = true;
- dataset.setValue(response.mUsername.first,
- AutofillValue.forText(response.mUsername.second));
+ AutofillId autofillId = getAutofillIdByResourceId(request, response.mUsername.first);
+ AutofillValue value = AutofillValue.forText(response.mUsername.second);
+ dataset.setValue(autofillId, value, newDatasetPresentation("dataset"));
}
if (response.mPassword != null) {
hasData = true;
- dataset.setValue(response.mPassword.first,
- AutofillValue.forText(response.mPassword.second));
+ AutofillId autofillId = getAutofillIdByResourceId(request, response.mPassword.first);
+ AutofillValue value = AutofillValue.forText(response.mPassword.second);
+ dataset.setValue(autofillId, value, newDatasetPresentation("dataset"));
}
if (hasData) {
FillResponse.Builder fillResponse = new FillResponse.Builder();
if (response.mIgnoredIds != null) {
- fillResponse.setIgnoredIds(response.mIgnoredIds);
+ int length = response.mIgnoredIds.length;
+ AutofillId[] requiredIds = new AutofillId[length];
+ for (int i = 0; i < length; i++) {
+ String resourceId = response.mIgnoredIds[i];
+ requiredIds[i] = getAutofillIdByResourceId(request, resourceId);
+ }
+ fillResponse.setIgnoredIds(requiredIds);
}
-
callback.onSuccess(fillResponse.addDataset(dataset.build()).build());
} else {
callback.onSuccess(null);
@@ -154,6 +161,16 @@
}
}
+ private AutofillId getAutofillIdByResourceId(FillRequest request, String resourceId)
+ throws Exception {
+ ViewNode node = AutofillTestHelper.findNodeByResourceId(request.getFillContexts(),
+ resourceId);
+ if (node == null) {
+ throw new AssertionError("No node with resource id " + resourceId);
+ }
+ return node.getAutofillId();
+ }
+
@Override
public void onSaveRequest(SaveRequest request, SaveCallback callback) {
// No current test should have triggered it...
@@ -162,9 +179,9 @@
}
static final class CannedResponse {
- private final Pair<AutofillId, String> mUsername;
- private final Pair<AutofillId, String> mPassword;
- private final AutofillId[] mIgnoredIds;
+ private final Pair<String, String> mUsername;
+ private final Pair<String, String> mPassword;
+ private final String[] mIgnoredIds;
private CannedResponse(@NonNull Builder builder) {
mUsername = builder.mUsername;
@@ -173,24 +190,24 @@
}
static class Builder {
- private Pair<AutofillId, String> mUsername;
- private Pair<AutofillId, String> mPassword;
- private AutofillId[] mIgnoredIds;
+ private Pair<String, String> mUsername;
+ private Pair<String, String> mPassword;
+ private String[] mIgnoredIds;
@NonNull
- Builder setUsername(@NonNull AutofillId id, @NonNull String value) {
+ Builder setUsername(@NonNull String id, @NonNull String value) {
mUsername = new Pair<>(id, value);
return this;
}
@NonNull
- Builder setPassword(@NonNull AutofillId id, @NonNull String value) {
+ Builder setPassword(@NonNull String id, @NonNull String value) {
mPassword = new Pair<>(id, value);
return this;
}
@NonNull
- Builder setIgnored(AutofillId... ids) {
+ Builder setIgnored(String... ids) {
mIgnoredIds = ids;
return this;
}