blob: ed8fe2bb431cdcd6340816a0c0d9296627dea6e2 [file] [log] [blame]
/*
* Copyright (C) 2017 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.autofillservice.cts;
import static android.autofillservice.cts.activities.DuplicateIdActivity.DUPLICATE_ID;
import static android.autofillservice.cts.testcore.CannedFillResponse.NO_RESPONSE;
import static android.autofillservice.cts.testcore.Helper.assertEqualsIgnoreSession;
import static android.autofillservice.cts.testcore.Helper.getActivityTitle;
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeTrue;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.autofillservice.cts.activities.DuplicateIdActivity;
import android.autofillservice.cts.commontests.AutoFillServiceTestCase;
import android.autofillservice.cts.testcore.AutofillActivityTestRule;
import android.autofillservice.cts.testcore.CannedFillResponse;
import android.autofillservice.cts.testcore.Helper;
import android.autofillservice.cts.testcore.InstrumentedAutoFillService;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.autofill.AutofillId;
import android.widget.EditText;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class DuplicateIdActivityTest
extends AutoFillServiceTestCase.AutoActivityLaunch<DuplicateIdActivity> {
private static final String TAG = "DuplicateIdActivityTest";
private static final AutofillActivityTestRule<DuplicateIdActivity> sActivityRule =
new AutofillActivityTestRule<DuplicateIdActivity>(DuplicateIdActivity.class);
@Override
protected AutofillActivityTestRule<DuplicateIdActivity> getActivityRule() {
return sActivityRule;
}
@Before
public void setup() throws Exception {
Helper.disableAutoRotation(mUiBot);
mUiBot.setScreenOrientation(0);
}
@After
public void teardown() {
Helper.allowAutoRotation();
}
/**
* Find the views that are tested from the structure in the request
*
* @param request The request
* @param expectedCount The expected number of children
*
* @return An array containing the two tested views
*/
private AssistStructure.ViewNode[] findViews(InstrumentedAutoFillService.FillRequest request,
int expectedCount, String activityTitle) {
AssistStructure.WindowNode windowNode = null;
int count = 0;
final int nodes = request.structure.getWindowNodeCount();
for (int i = 0; i < nodes; ++i) {
if (TextUtils.equals(request.structure.getWindowNodeAt(i).getTitle(), activityTitle)) {
windowNode = request.structure.getWindowNodeAt(i);
count++;
}
}
assertThat(count).isEqualTo(1);
AssistStructure.ViewNode rootNode = windowNode.getRootViewNode();
assertThat(rootNode.getChildCount()).isEqualTo(expectedCount);
final ViewNode[] viewNodes = new AssistStructure.ViewNode[expectedCount];
for (int i = 0; i < expectedCount; i++) {
viewNodes[i] = rootNode.getChildAt(i);
}
return viewNodes;
}
@FlakyTest(bugId = 259873363) // TODO: find out why this test fails
@Test
public void testDoNotRestoreDuplicateAutofillIds() throws Exception {
assumeTrue("Rotation is supported", Helper.isRotationSupported(mContext));
assumeTrue("Device state is not REAR_DISPLAY",
!Helper.isDeviceInState(mContext, Helper.DeviceStateEnum.REAR_DISPLAY));
enableService();
sReplier.addResponse(new CannedFillResponse.Builder()
.addDataset(new CannedFillResponse.CannedDataset.Builder()
.setField(DUPLICATE_ID, "value")
.setPresentation(createPresentation("dataset"))
.build())
.build());
// Select field to start autofill
runShellCommand("input keyevent KEYCODE_TAB");
final InstrumentedAutoFillService.FillRequest request1 = sReplier.getNextFillRequest();
Log.v(TAG, "request1: " + request1);
final String activityTitle = getActivity().getPackageName() + "/"
+ getActivityTitle(InstrumentationRegistry.getInstrumentation(), getActivity());
final AssistStructure.ViewNode[] views1 = findViews(request1, 2, activityTitle);
final AssistStructure.ViewNode view1 = views1[0];
final AssistStructure.ViewNode view2 = views1[1];
final AutofillId id1 = view1.getAutofillId();
final AutofillId id2 = view2.getAutofillId();
Log.v(TAG, "view1=" + id1);
Log.v(TAG, "view2=" + id2);
// Both checkboxes use the same id
assertThat(view1.getId()).isEqualTo(view2.getId());
// They got different autofill ids though
assertThat(id1).isNotEqualTo(id2);
// TODO(279618346): check the reason why focus state does not restore
// Force rotation to force onDestroy->onCreate cycle
mUiBot.setScreenOrientation(1);
// Wait context and Views being recreated in rotation
mUiBot.assertShownByRelativeId(DUPLICATE_ID);
// Select a new field to trigger new partition (because server return null on 1st response)
sReplier.addResponse(NO_RESPONSE);
final DuplicateIdActivity activity = getActivity();
final EditText child = new EditText(activity);
activity.syncRunOnUiThread(() -> {
final View sibling = activity.findViewById(R.id.duplicate_id);
final ViewGroup parent = (ViewGroup) sibling.getParent();
parent.addView(child);
child.requestFocus();
});
final InstrumentedAutoFillService.FillRequest request3 = sReplier.getNextFillRequest();
Log.v(TAG, "request3: " + request3);
final AssistStructure.ViewNode[] views2 = findViews(request3, 3, activityTitle);
final AssistStructure.ViewNode recreatedView1 = views2[0];
final AssistStructure.ViewNode recreatedView2 = views2[1];
final AssistStructure.ViewNode newView1 = views2[2];
final AutofillId recreatedId1 = recreatedView1.getAutofillId();
final AutofillId recreatedId2 = recreatedView2.getAutofillId();
final AutofillId newId1 = newView1.getAutofillId();
Log.v(TAG, "restored view1=" + recreatedId1);
Log.v(TAG, "restored view2=" + recreatedId2);
Log.v(TAG, "new view1=" + newId1);
// For the restoring logic the two views are the same. Hence it might happen that the first
// view is restored with the autofill id of the second view or the other way round.
// We just need
// - to restore as many views as we can (i.e. one)
// - make sure the autofill ids are still unique after
final boolean view1WasRestored = (recreatedId1.equalsIgnoreSession(id1)
|| recreatedId1.equalsIgnoreSession(id2));
final boolean view2WasRestored = (recreatedId2.equalsIgnoreSession(id1)
|| recreatedId2.equalsIgnoreSession(id2));
// One id was restored
assertThat(view1WasRestored || view2WasRestored).isTrue();
// The views still have different autofill ids
assertThat(recreatedId1).isNotEqualTo(recreatedId2);
// Assert id of new view
assertEqualsIgnoreSession(newId1, child.getAutofillId());
}
}