Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 98ffc145 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Fix and enable the fail Autofill Perf Tests."

parents 823982d3 2c437a6b
Loading
Loading
Loading
Loading
+81 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+35 −1
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ import androidx.annotation.NonNull;
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 class AutofillTestWatcher extends TestWatcher {
        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 @@ final class AutofillTestWatcher extends TestWatcher {
     * 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 @@ final class AutofillTestWatcher extends TestWatcher {
                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);
+17 −17
Original line number Diff line number Diff line
@@ -28,12 +28,14 @@ import androidx.test.filters.LargeTest;

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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
        // 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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
    /**
     * 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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {

        // 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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
    /**
     * 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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {

        // 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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
    @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 class LoginTest extends AbstractAutofillPerfTestCase {
    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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
        }
    }

    // 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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
        // 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);

+33 −16
Original line number Diff line number Diff line
@@ -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 @@ public class MyAutofillService extends AutofillService {
            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 @@ public class MyAutofillService extends AutofillService {
        }
    }

    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 @@ public class MyAutofillService extends AutofillService {
    }

    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 @@ public class MyAutofillService extends AutofillService {
        }

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