Fixed DuplicateIdActivityTest.testDoNotRestoreDuplicateAutofillIds()
It was not consuming the 3rd fill request, which could fail the test due to
race conditions.
Bug: 77271640
Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases \
-t DuplicateIdActivityTest#testDoNotRestoreDuplicateAutofillIds
Change-Id: I22f74daaf8d07c7c2804eb5a81c972c9dee0a2f7
Merged-In: I5f8af54b2f3824250b4b1da4e68dd167f8db2b6b
(cherry-picked from commit 1acb8aafc5427e625c9e4868f0f67da648495617)
Change-Id: I896a7dd12993c67331f6c98b46e8d99f5dfeb793
diff --git a/tests/autofillservice/res/layout/duplicate_id_layout.xml b/tests/autofillservice/res/layout/duplicate_id_layout.xml
index a5643ea..4d409a2 100644
--- a/tests/autofillservice/res/layout/duplicate_id_layout.xml
+++ b/tests/autofillservice/res/layout/duplicate_id_layout.xml
@@ -17,17 +17,21 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:id="@+id/duplicate_id"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:text="cb1" /> <!-- text is set just for debugging purposes -->
<CheckBox
android:id="@+id/duplicate_id"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:text="cb2"/> <!-- text is set just for debugging purposes -->
</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivity.java b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivity.java
index 31ac8f7..90871ca 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivity.java
@@ -20,7 +20,7 @@
import android.util.Log;
public class DuplicateIdActivity extends AbstractAutoFillActivity {
- private static final String LOG_TAG = DuplicateIdActivity.class.getSimpleName();
+ private static final String TAG = "DuplicateIdActivity";
static final String DUPLICATE_ID = "duplicate_id";
@@ -28,9 +28,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (savedInstanceState != null) {
- Log.i(LOG_TAG, "onCreate(" + savedInstanceState + ")");
- }
+ Log.v(TAG, "onCreate(" + savedInstanceState + ")");
setContentView(R.layout.duplicate_id_layout);
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
index 29ec2b1..846dcc4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
@@ -89,13 +89,14 @@
// Select field to start autofill
runShellCommand("input keyevent KEYCODE_TAB");
- InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
+ final InstrumentedAutoFillService.FillRequest request1 = sReplier.getNextFillRequest();
+ Log.v(LOG_TAG, "request1: " + request1);
- AssistStructure.ViewNode[] views = findViews(request);
- AssistStructure.ViewNode view1 = views[0];
- AssistStructure.ViewNode view2 = views[1];
- AutofillId id1 = view1.getAutofillId();
- AutofillId id2 = view2.getAutofillId();
+ final AssistStructure.ViewNode[] views1 = findViews(request1);
+ final AssistStructure.ViewNode view1 = views1[0];
+ final AssistStructure.ViewNode view2 = views1[1];
+ final AutofillId id1 = view1.getAutofillId();
+ final AutofillId id2 = view2.getAutofillId();
Log.i(LOG_TAG, "view1=" + id1);
Log.i(LOG_TAG, "view2=" + id2);
@@ -106,38 +107,39 @@
// They got different autofill ids though
assertThat(id1).isNotEqualTo(id2);
+ // Because service returned a null response, rotation will trigger another request.
sReplier.addResponse(NO_RESPONSE);
-
// Force rotation to force onDestroy->onCreate cycle
mUiBot.setScreenOrientation(1);
// Wait context and Views being recreated in rotation
mUiBot.assertShownByRelativeId(DUPLICATE_ID);
+ // Ignore 2nd request.
+ final InstrumentedAutoFillService.FillRequest request2 = sReplier.getNextFillRequest();
+ Log.v(LOG_TAG, "request2: " + request2);
- // Because service returned a null response, rotation will trigger another request.
+ // Select other field to trigger new partition (because server didn't return 2nd field
+ // on 1st response)
sReplier.addResponse(NO_RESPONSE);
-
- // Select other field to trigger new partition
runShellCommand("input keyevent KEYCODE_TAB");
- request = sReplier.getNextFillRequest();
-
- // Ignore 2nd request.
- sReplier.getNextFillRequest();
-
- views = findViews(request);
- AutofillId recreatedId1 = views[0].getAutofillId();
- AutofillId recreatedId2 = views[1].getAutofillId();
+ final InstrumentedAutoFillService.FillRequest request3 = sReplier.getNextFillRequest();
+ Log.v(LOG_TAG, "request3: " + request3);
+ final AssistStructure.ViewNode[] views2 = findViews(request3);
+ final AssistStructure.ViewNode recreatedView1 = views2[0];
+ final AssistStructure.ViewNode recreatedView2 = views2[1];
+ final AutofillId recreatedId1 = recreatedView1.getAutofillId();
+ final AutofillId recreatedId2 = recreatedView2.getAutofillId();
Log.i(LOG_TAG, "restored view1=" + recreatedId1);
Log.i(LOG_TAG, "restored view2=" + recreatedId2);
// For the restoring logic the two views are the same. Hence it might happen that the first
- // view is restored with the id of the second view or the other way round.
+ // 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
- boolean view1WasRestored = (recreatedId1.equals(id1) || recreatedId1.equals(id2));
- boolean view2WasRestored = (recreatedId2.equals(id1) || recreatedId2.equals(id2));
+ final boolean view1WasRestored = (recreatedId1.equals(id1) || recreatedId1.equals(id2));
+ final boolean view2WasRestored = (recreatedId2.equals(id1) || recreatedId2.equals(id2));
// One id was restored
assertThat(view1WasRestored || view2WasRestored).isTrue();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 7934730..4e5418b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -156,19 +156,37 @@
return hasHint(view.getAutofillHints(), id);
};
+ private static String toString(AssistStructure structure, StringBuilder builder) {
+ builder.append("[component=").append(structure.getActivityComponent());
+ final int nodes = structure.getWindowNodeCount();
+ for (int i = 0; i < nodes; i++) {
+ final WindowNode windowNode = structure.getWindowNodeAt(i);
+ dump(builder, windowNode.getRootViewNode(), " ", 0);
+ }
+ return builder.append(']').toString();
+ }
+
+ @NonNull
+ static String toString(@NonNull AssistStructure structure) {
+ return toString(structure, new StringBuilder());
+ }
+
+ @Nullable
+ static String toString(@Nullable AutofillValue value) {
+ if (value == null) return null;
+ if (value.isText()) {
+ // We don't care about PII...
+ final CharSequence text = value.getTextValue();
+ return text == null ? null : text.toString();
+ }
+ return value.toString();
+ }
+
/**
* Dump the assist structure on logcat.
*/
static void dumpStructure(String message, AssistStructure structure) {
- final StringBuffer buffer = new StringBuffer(message)
- .append(": component=")
- .append(structure.getActivityComponent());
- final int nodes = structure.getWindowNodeCount();
- for (int i = 0; i < nodes; i++) {
- final WindowNode windowNode = structure.getWindowNodeAt(i);
- dump(buffer, windowNode.getRootViewNode(), " ", 0);
- }
- Log.i(TAG, buffer.toString());
+ Log.i(TAG, toString(structure, new StringBuilder(message)));
}
/**
@@ -194,39 +212,69 @@
SettingsHelper.syncSet(context, USER_SETUP_COMPLETE, complete ? "1" : null);
}
- private static void dump(StringBuffer buffer, ViewNode node, String prefix, int childId) {
+ private static void dump(@NonNull StringBuilder builder, @NonNull ViewNode node,
+ @NonNull String prefix, int childId) {
final int childrenSize = node.getChildCount();
- buffer.append("\n").append(prefix)
- .append('#').append(childId).append(':')
- .append("resId=").append(node.getIdEntry())
- .append(" class=").append(node.getClassName())
- .append(" text=").append(node.getText())
- .append(" class=").append(node.getClassName())
- .append(" webDomain=").append(node.getWebDomain())
- .append(" #children=").append(childrenSize);
-
- buffer.append("\n").append(prefix)
- .append(" afId=").append(node.getAutofillId())
- .append(" afType=").append(node.getAutofillType())
- .append(" afValue=").append(node.getAutofillValue())
- .append(" checked=").append(node.isChecked())
- .append(" focused=").append(node.isFocused());
-
+ builder.append("\n").append(prefix)
+ .append("child #").append(childId).append(':');
+ append(builder, "afId", node.getAutofillId());
+ append(builder, "afType", node.getAutofillType());
+ append(builder, "afValue", toString(node.getAutofillValue()));
+ append(builder, "resId", node.getIdEntry());
+ append(builder, "class", node.getClassName());
+ append(builder, "text", node.getText());
+ append(builder, "webDomain", node.getWebDomain());
+ append(builder, "checked", node.isChecked());
+ append(builder, "focused", node.isFocused());
final HtmlInfo htmlInfo = node.getHtmlInfo();
if (htmlInfo != null) {
- buffer.append("\nHtmlInfo: tag=").append(htmlInfo.getTag())
- .append(", attrs: ").append(htmlInfo.getAttributes());
+ builder.append(", HtmlInfo[tag=").append(htmlInfo.getTag())
+ .append(", attrs: ").append(htmlInfo.getAttributes()).append(']');
}
-
- prefix += " ";
if (childrenSize > 0) {
- for (int i = 0; i < childrenSize; i++) {
- dump(buffer, node.getChildAt(i), prefix, i);
+ append(builder, "#children", childrenSize).append("\n").append(prefix);
+ prefix += " ";
+ if (childrenSize > 0) {
+ for (int i = 0; i < childrenSize; i++) {
+ dump(builder, node.getChildAt(i), prefix, i);
+ }
}
}
}
/**
+ * Appends a field value to a {@link StringBuilder} when it's not {@code null}.
+ */
+ @NonNull
+ static StringBuilder append(@NonNull StringBuilder builder, @NonNull String field,
+ @Nullable Object value) {
+ if (value == null) return builder;
+
+ if ((value instanceof Boolean) && ((Boolean) value)) {
+ return builder.append(", ").append(field);
+ }
+
+ if (value instanceof Integer && ((Integer) value) == 0
+ || value instanceof CharSequence && TextUtils.isEmpty((CharSequence) value)) {
+ return builder;
+ }
+
+ return builder.append(", ").append(field).append('=').append(value);
+ }
+
+ /**
+ * Appends a field value to a {@link StringBuilder} when it's {@code true}.
+ */
+ @NonNull
+ static StringBuilder append(@NonNull StringBuilder builder, @NonNull String field,
+ boolean value) {
+ if (value) {
+ builder.append(", ").append(field);
+ }
+ return builder;
+ }
+
+ /**
* Gets a node if it matches the filter criteria for the given id.
*/
static ViewNode findNodeByFilter(@NonNull AssistStructure structure, @NonNull Object id,
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
index 42b6b41..6b47c9f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -293,12 +293,13 @@
this.cancellationSignal = cancellationSignal;
this.callback = callback;
this.flags = flags;
- structure = contexts.get(contexts.size() - 1).getStructure();
+ this.structure = contexts.get(contexts.size() - 1).getStructure();
}
@Override
public String toString() {
- return "FillRequest:" + getActivityName(contexts);
+ return "FillRequest[activity=" + getActivityName(contexts) + ", flags=" + flags
+ + ", bundle=" + data + ", structure=" + Helper.toString(structure) + "]";
}
}