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

Commit 236c6264 authored by Felipe Leme's avatar Felipe Leme
Browse files

Make Autofill PERF tests more lenient to error.

These tests were based on CTS autofill tests, where correctness is the priority
(for example, there should be no "orphan" onFillRequest() call) and the number
of UI interactions is small.

But that "correctness" model doesn't work well on PERF, because the
onFillRequest() calls are handled in the main thread and the main test loop can
make hundreds of UI calls. For example, if testFocus_autofillUsernameOnly()
doesn't set the ignored id on the password field, each focus on that field would
result in a new onFillRequest(), which is not called right away. As such, the
main loop would finish, but subsequent tests would hang until all those calls
are made.

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

Bug: 38345816

Change-Id: I83a311e719966dcf41e86142952075e68d0c64bf
parent f6373528
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -86,8 +86,17 @@ public abstract class AbstractAutofillPerfTestCase {
    }

    @Before
    public void resetStaticState() {
    public void enableService() {
        MyAutofillService.resetStaticState();
        MyAutofillService.setEnabled(true);
    }

    @After
    public void disableService() {
        // Must disable service so calls are ignored in case of errors during the test case;
        // otherwise, other tests will fail because these calls are made in the UI thread (as both
        // the service, the tests, and the app run in the same process).
        MyAutofillService.setEnabled(false);
    }

    /**
+0 −19
Original line number Diff line number Diff line
@@ -82,9 +82,6 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
        setService();

        focusTest(true);

        // Sanity check
        MyAutofillService.assertNoAsyncErrors();
    }

    /**
@@ -99,9 +96,6 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
        setService();

        focusTest(true);

        // Sanity check
        MyAutofillService.assertNoAsyncErrors();
    }

    /**
@@ -117,9 +111,6 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
        setService();

        focusTest(true);

        // Sanity check
        MyAutofillService.assertNoAsyncErrors();
    }

    private void focusTest(boolean waitForService) throws Throwable {
@@ -158,9 +149,6 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
        setService();

        changeTest(true);

        // Sanity check
        MyAutofillService.assertNoAsyncErrors();
    }

    /**
@@ -175,9 +163,6 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
        setService();

        changeTest(true);

        // Sanity check
        MyAutofillService.assertNoAsyncErrors();
    }

    /**
@@ -193,9 +178,6 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
        setService();

        changeTest(true);

        // Sanity check
        MyAutofillService.assertNoAsyncErrors();
    }

    private void changeTest(boolean waitForService) throws Throwable {
@@ -254,7 +236,6 @@ public class LoginTest extends AbstractAutofillPerfTestCase {

            // Sanity check
            callback.assertNoAsyncErrors();
            MyAutofillService.assertNoAsyncErrors();
        } finally {
            mAfm.unregisterCallback(callback);
        }
+51 −45
Original line number Diff line number Diff line
@@ -52,7 +52,8 @@ public class MyAutofillService extends AutofillService {
    private static final BlockingQueue<FillRequest> sFillRequests = new LinkedBlockingQueue<>();
    private static final BlockingQueue<CannedResponse> sCannedResponses =
            new LinkedBlockingQueue<>();
    private static final List<String> sAsyncErrors = new ArrayList<>();

    private static boolean sEnabled;

    /**
     * Resets the static state associated with the service.
@@ -60,17 +61,15 @@ public class MyAutofillService extends AutofillService {
    static void resetStaticState() {
        sFillRequests.clear();
        sCannedResponses.clear();
        sAsyncErrors.clear();
        sEnabled = false;
    }

    /**
     * Throws an exception if an error happened asynchronously while handing
     * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)}.
     * Sets whether the service is enabled or not - when disabled, calls to
     * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} will be ignored.
     */
    static void assertNoAsyncErrors() {
       if (!sAsyncErrors.isEmpty()) {
           throw new IllegalStateException(sAsyncErrors.size() + " errors: " + sAsyncErrors);
       }
    static void setEnabled(boolean enabled) {
        sEnabled = enabled;
    }

    /**
@@ -96,19 +95,27 @@ public class MyAutofillService extends AutofillService {
    @Override
    public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
            FillCallback callback) {
        CannedResponse response = null;
        try {
            response = sCannedResponses.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS);
            handleRequest(request, callback);
        } catch (InterruptedException e) {
            addAsyncError("onFillRequest() interrupted");
            Thread.currentThread().interrupt();
            onError("onFillRequest() interrupted", e, callback);
        } catch (Exception e) {
            onError("exception on onFillRequest()", e, callback);
        }
    }


    private void handleRequest(FillRequest request, FillCallback callback) throws Exception {
        if (!sEnabled) {
            onError("ignoring onFillRequest(): service is disabled", callback);
            return;
        }
        CannedResponse response = sCannedResponses.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS);
        if (response == null) {
            addAsyncError("onFillRequest() called without setting a response");
            onError("ignoring onFillRequest(): response not set", callback);
            return;
        }
        try {
        Dataset.Builder dataset = new Dataset.Builder(newDatasetPresentation("dataset"));
        boolean hasData = false;
        if (response.mUsername != null) {
@@ -131,16 +138,16 @@ public class MyAutofillService extends AutofillService {
        } else {
            callback.onSuccess(null);
        }
        } catch (Exception e) {
            addAsyncError(e, callback);
        if (!sFillRequests.offer(request, TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
            Log.w(TAG, "could not offer request in " + TIMEOUT_MS + "ms");
        }
        sFillRequests.offer(request);
    }

    @Override
    public void onSaveRequest(SaveRequest request, SaveCallback callback) {
        // No current test should have triggered it...
        callback.onFailure("should not have called onSave");
        Log.e(TAG, "onSaveRequest() should not have been called");
        callback.onFailure("onSaveRequest() should not have been called");
    }

    static final class CannedResponse {
@@ -191,15 +198,14 @@ public class MyAutofillService extends AutofillService {
        return new CannedResponse.Builder();
    }

    private void addAsyncError(@NonNull String error) {
        sAsyncErrors.add(error);
        Log.e(TAG, error);
    private void onError(@NonNull String msg, @NonNull FillCallback callback) {
        Log.e(TAG, msg);
        callback.onFailure(msg);
    }

    private void addAsyncError(@NonNull Exception e, @NonNull FillCallback callback) {
        String msg = e.toString();
        sAsyncErrors.add(msg);
        Log.e(TAG, "async error", e);
    private void onError(@NonNull String msg, @NonNull Exception e,
            @NonNull FillCallback callback) {
        Log.e(TAG, msg, e);
        callback.onFailure(msg);
    }