1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.contentcaptureservice.cts;
17 
18 import static android.contentcaptureservice.cts.Helper.MY_EPOCH;
19 import static android.contentcaptureservice.cts.Helper.TAG;
20 import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
21 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_PAUSED;
22 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_RESUMED;
23 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
24 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
25 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
26 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED;
27 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING;
28 
29 import static com.google.common.truth.Truth.assertThat;
30 import static com.google.common.truth.Truth.assertWithMessage;
31 
32 import android.content.ComponentName;
33 import android.content.LocusId;
34 import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
35 import android.util.Log;
36 import android.view.Display;
37 import android.view.View;
38 import android.view.autofill.AutofillId;
39 import android.view.contentcapture.ContentCaptureEvent;
40 import android.view.contentcapture.ContentCaptureSession;
41 import android.view.contentcapture.ContentCaptureSessionId;
42 import android.view.contentcapture.ViewNode;
43 
44 import androidx.annotation.NonNull;
45 import androidx.annotation.Nullable;
46 
47 import java.util.List;
48 
49 /**
50  * Helper for common assertions.
51  */
52 final class Assertions {
53 
54     /**
55      * Asserts a session belongs to the right activity.
56      */
assertRightActivity(@onNull Session session, @NonNull ContentCaptureSessionId expectedSessionId, @NonNull AbstractContentCaptureActivity activity)57     public static void assertRightActivity(@NonNull Session session,
58             @NonNull ContentCaptureSessionId expectedSessionId,
59             @NonNull AbstractContentCaptureActivity activity) {
60         assertRightActivity(session, expectedSessionId, activity.getComponentName());
61     }
62 
63     /**
64      * Asserts a session belongs to the right activity.
65      */
assertRightActivity(@onNull Session session, @NonNull ContentCaptureSessionId expectedSessionId, @NonNull ComponentName componentName)66     public static void assertRightActivity(@NonNull Session session,
67             @NonNull ContentCaptureSessionId expectedSessionId,
68             @NonNull ComponentName componentName) {
69         assertWithMessage("wrong activity for %s", session)
70                 .that(session.context.getActivityComponent()).isEqualTo(componentName);
71         // TODO(b/123540602): merge both or replace check above by:
72         //  assertMainSessionContext(session, activity);
73         assertThat(session.id).isEqualTo(expectedSessionId);
74     }
75 
76     /**
77      * Asserts the context of a main session.
78      */
assertMainSessionContext(@onNull Session session, @NonNull AbstractContentCaptureActivity activity)79     public static void assertMainSessionContext(@NonNull Session session,
80             @NonNull AbstractContentCaptureActivity activity) {
81         assertMainSessionContext(session, activity, /* expectedFlags= */ 0);
82     }
83 
84     /**
85      * Asserts the context of a main session.
86      */
assertMainSessionContext(@onNull Session session, @NonNull AbstractContentCaptureActivity activity, int expectedFlags)87     public static void assertMainSessionContext(@NonNull Session session,
88             @NonNull AbstractContentCaptureActivity activity, int expectedFlags) {
89         assertWithMessage("no context on %s", session).that(session.context).isNotNull();
90         assertWithMessage("wrong activity for %s", session)
91                 .that(session.context.getActivityComponent())
92                 .isEqualTo(activity.getComponentName());
93         assertWithMessage("context for session %s should have displayId", session)
94                 .that(session.context.getDisplayId()).isNotEqualTo(Display.INVALID_DISPLAY);
95         assertWithMessage("wrong task id for session %s", session)
96                 .that(session.context.getTaskId()).isEqualTo(activity.getRealTaskId());
97         assertWithMessage("wrong flags on context for session %s", session)
98                 .that(session.context.getFlags()).isEqualTo(expectedFlags);
99         assertWithMessage("context for session %s should not have ID", session)
100                 .that(session.context.getLocusId()).isNull();
101         assertWithMessage("context for session %s should not have extras", session)
102                 .that(session.context.getExtras()).isNull();
103     }
104 
105     /**
106      * Asserts the invariants of a child session.
107      */
assertChildSessionContext(@onNull Session session)108     public static void assertChildSessionContext(@NonNull Session session) {
109         assertWithMessage("no context on %s", session).that(session.context).isNotNull();
110         assertWithMessage("context for session %s should not have component", session)
111                 .that(session.context.getActivityComponent()).isNull();
112         assertWithMessage("context for session %s should not have displayId", session)
113                 .that(session.context.getDisplayId()).isEqualTo(Display.INVALID_DISPLAY);
114         assertWithMessage("context for session %s should not have taskId", session)
115                 .that(session.context.getTaskId()).isEqualTo(0);
116         assertWithMessage("context for session %s should not have flags", session)
117                 .that(session.context.getFlags()).isEqualTo(0);
118     }
119 
120     /**
121      * Asserts the invariants of a child session.
122      */
assertChildSessionContext(@onNull Session session, @NonNull String expectedId)123     public static void assertChildSessionContext(@NonNull Session session,
124             @NonNull String expectedId) {
125         assertChildSessionContext(session);
126         assertThat(session.context.getLocusId()).isEqualTo(new LocusId(expectedId));
127     }
128 
129     /**
130      * Asserts a session belongs to the right parent
131      */
assertRightRelationship(@onNull Session parent, @NonNull Session child)132     public static void assertRightRelationship(@NonNull Session parent, @NonNull Session child) {
133         final ContentCaptureSessionId expectedParentId = parent.id;
134         assertWithMessage("No id on parent session %s", parent).that(expectedParentId).isNotNull();
135         assertWithMessage("No context on child session %s", child).that(child.context).isNotNull();
136         final ContentCaptureSessionId actualParentId = child.context.getParentSessionId();
137         assertWithMessage("No parent id on context %s of child session %s", child.context, child)
138                 .that(actualParentId).isNotNull();
139         assertWithMessage("id of parent session doesn't match child").that(actualParentId)
140                 .isEqualTo(expectedParentId);
141     }
142 
143     /**
144      * Asserts the contents of a {@link #TYPE_VIEW_APPEARED} event, without checking for parent id.
145      */
assertViewWithUnknownParentAppeared( @onNull List<ContentCaptureEvent> events, int index, @NonNull View expectedView)146     public static ViewNode assertViewWithUnknownParentAppeared(
147             @NonNull List<ContentCaptureEvent> events, int index, @NonNull View expectedView) {
148         return assertViewWithUnknownParentAppeared(events, index, expectedView,
149                 /* expectedText= */ null);
150     }
151 
152     /**
153      * Asserts the contents of a {@link #TYPE_VIEW_APPEARED} event for a decor view.
154      *
155      * <P>The decor view is typically internal, so there isn't much we can assert, other than its
156      * autofill id.
157      */
assertDecorViewAppeared(@onNull List<ContentCaptureEvent> events, int index, @NonNull View expectedDecorView)158     public static void assertDecorViewAppeared(@NonNull List<ContentCaptureEvent> events,
159             int index, @NonNull View expectedDecorView) {
160         final ContentCaptureEvent event = assertViewAppeared(events, index);
161         assertWithMessage("wrong autofill id on %s (%s)", event, index)
162                 .that(event.getViewNode().getAutofillId())
163                 .isEqualTo(expectedDecorView.getAutofillId());
164     }
165 
166     /**
167      * Asserts the contents of a {@link #TYPE_VIEW_APPEARED} event, without checking for parent id.
168      */
assertViewWithUnknownParentAppeared( @onNull List<ContentCaptureEvent> events, int index, @NonNull View expectedView, @Nullable String expectedText)169     public static ViewNode assertViewWithUnknownParentAppeared(
170             @NonNull List<ContentCaptureEvent> events, int index, @NonNull View expectedView,
171             @Nullable String expectedText) {
172         final ContentCaptureEvent event = assertViewAppeared(events, index);
173         final ViewNode node = event.getViewNode();
174 
175         assertWithMessage("wrong class on %s (%s)", event, index).that(node.getClassName())
176                 .isEqualTo(expectedView.getClass().getName());
177         assertWithMessage("wrong autofill id on %s (%s)", event, index).that(node.getAutofillId())
178                 .isEqualTo(expectedView.getAutofillId());
179 
180         if (expectedText != null) {
181             assertWithMessage("wrong text on %s (%s)", event, index).that(node.getText().toString())
182                     .isEqualTo(expectedText);
183         }
184         // TODO(b/123540602): test more fields, like resource id
185         return node;
186     }
187 
188     /**
189      * Asserts the contents of a {@link #TYPE_VIEW_APPEARED} event, without checking for parent id.
190      */
assertViewAppeared(@onNull List<ContentCaptureEvent> events, int index)191     public static ContentCaptureEvent assertViewAppeared(@NonNull List<ContentCaptureEvent> events,
192             int index) {
193         final ContentCaptureEvent event = getEvent(events, index, TYPE_VIEW_APPEARED);
194         final ViewNode node = event.getViewNode();
195         assertThat(node).isNotNull();
196         return event;
197     }
198 
199     /**
200      * Asserts the contents of a {@link #TYPE_VIEW_APPEARED} event.
201      */
assertViewAppeared(@onNull List<ContentCaptureEvent> events, int index, @NonNull View expectedView, @Nullable AutofillId expectedParentId, @Nullable String expectedText)202     public static void assertViewAppeared(@NonNull List<ContentCaptureEvent> events, int index,
203             @NonNull View expectedView, @Nullable AutofillId expectedParentId,
204             @Nullable String expectedText) {
205         final ViewNode node = assertViewWithUnknownParentAppeared(events, index, expectedView,
206                 expectedText);
207         assertWithMessage("wrong parent autofill id on %s (%s)", events.get(index), index)
208             .that(node.getParentAutofillId()).isEqualTo(expectedParentId);
209     }
210 
211     /**
212      * Asserts the contents of a {@link #TYPE_VIEW_APPEARED} event.
213      */
assertViewAppeared(@onNull List<ContentCaptureEvent> events, int index, @NonNull View expectedView, @Nullable AutofillId expectedParentId)214     public static void assertViewAppeared(@NonNull List<ContentCaptureEvent> events, int index,
215             @NonNull View expectedView, @Nullable AutofillId expectedParentId) {
216         assertViewAppeared(events, index, expectedView, expectedParentId, /* expectedText= */ null);
217     }
218 
219     /**
220      * Asserts the contents of a {@link #TYPE_VIEW_APPEARED} event.
221      */
assertViewAppeared(@onNull List<ContentCaptureEvent> events, int index, @NonNull ContentCaptureSessionId expectedSessionId, @NonNull View expectedView, @Nullable AutofillId expectedParentId)222     public static void assertViewAppeared(@NonNull List<ContentCaptureEvent> events, int index,
223             @NonNull ContentCaptureSessionId expectedSessionId,
224             @NonNull View expectedView, @Nullable AutofillId expectedParentId) {
225         assertViewAppeared(events, index, expectedView, expectedParentId);
226         assertSessionId(expectedSessionId, expectedView);
227     }
228 
229     /**
230      * Asserts the contents of a {@link #TYPE_VIEW_TREE_APPEARING} event.
231      */
assertViewTreeStarted(@onNull List<ContentCaptureEvent> events, int index)232     public static void assertViewTreeStarted(@NonNull List<ContentCaptureEvent> events,
233             int index) {
234         assertSessionLevelEvent(events, index, TYPE_VIEW_TREE_APPEARING);
235     }
236 
237     /**
238      * Asserts the contents of a {@link #TYPE_VIEW_TREE_APPEARED} event.
239      */
assertViewTreeFinished(@onNull List<ContentCaptureEvent> events, int index)240     public static void assertViewTreeFinished(@NonNull List<ContentCaptureEvent> events,
241             int index) {
242         assertSessionLevelEvent(events, index, TYPE_VIEW_TREE_APPEARED);
243     }
244 
assertSessionLevelEvent(@onNull List<ContentCaptureEvent> events, int index, int expectedType)245     private static void assertSessionLevelEvent(@NonNull List<ContentCaptureEvent> events,
246             int index, int expectedType) {
247         final ContentCaptureEvent event = getEvent(events, index, expectedType);
248         assertWithMessage("event %s (index %s) should not have a ViewNode", event, index)
249                 .that(event.getViewNode()).isNull();
250         assertWithMessage("event %s (index %s) should not have text", event, index)
251                 .that(event.getViewNode()).isNull();
252         assertWithMessage("event %s (index %s) should not have an autofillId", event, index)
253                 .that(event.getId()).isNull();
254         assertWithMessage("event %s (index %s) should not have autofillIds", event, index)
255                 .that(event.getIds()).isNull();
256     }
257 
258     /**
259      * Asserts the contents of a {@link #TYPE_SESSION_RESUMED} event.
260      */
assertSessionResumed(@onNull List<ContentCaptureEvent> events, int index)261     public static void assertSessionResumed(@NonNull List<ContentCaptureEvent> events,
262             int index) {
263         assertSessionLevelEvent(events, index, TYPE_SESSION_RESUMED);
264     }
265 
266     /**
267      * Asserts the contents of a {@link #TYPE_SESSION_PAUSED} event.
268      */
assertSessionPaused(@onNull List<ContentCaptureEvent> events, int index)269     public static void assertSessionPaused(@NonNull List<ContentCaptureEvent> events,
270             int index) {
271         assertSessionLevelEvent(events, index, TYPE_SESSION_PAUSED);
272     }
273 
274     /**
275      * Asserts that a session for the given activity has no view-level events, just
276      * {@link #TYPE_SESSION_RESUMED} and {@link #TYPE_SESSION_PAUSED}.
277      */
assertNoViewLevelEvents(@onNull Session session, @NonNull AbstractContentCaptureActivity activity)278     public static void assertNoViewLevelEvents(@NonNull Session session,
279             @NonNull AbstractContentCaptureActivity activity) {
280         assertRightActivity(session, session.id, activity);
281         final List<ContentCaptureEvent> events = session.getEvents();
282         Log.v(TAG, "events on " + activity + ": " + events);
283         assertThat(events).hasSize(2);
284         assertSessionResumed(events, 0);
285         assertSessionPaused(events, 1);
286     }
287 
288     /**
289      * Asserts that a session for the given activity has events at all.
290      */
assertNoEvents(@onNull Session session, @NonNull ComponentName componentName)291     public static void assertNoEvents(@NonNull Session session,
292             @NonNull ComponentName componentName) {
293         assertRightActivity(session, session.id, componentName);
294         assertThat(session.getEvents()).isEmpty();
295     }
296 
297     /**
298      * Asserts that the events received by the service optionally contains the
299      * {@code TYPE_VIEW_DISAPPEARED} events, as they might have not been generated if the views
300      * disappeared after the activity stopped.
301      *
302      * @param events events received by the service.
303      * @param minimumSize size of events received if activity stopped before views disappeared
304      * @param expectedIds ids of views that might have disappeared.
305      *
306      * @return whether the view disappeared events were generated
307      */
308     // TODO(b/123540067, 122315042): remove this method if we could make it deterministic, and
309     // inline the assertions (or rename / change its logic)
assertViewsOptionallyDisappeared( @onNull List<ContentCaptureEvent> events, int minimumSize, @NonNull AutofillId... expectedIds)310     public static boolean assertViewsOptionallyDisappeared(
311             @NonNull List<ContentCaptureEvent> events, int minimumSize,
312             @NonNull AutofillId... expectedIds) {
313         final int actualSize = events.size();
314         if (actualSize == minimumSize) {
315             // Activity stopped before TYPE_VIEW_DISAPPEARED were sent.
316             return false;
317         }
318         assertThat(events).hasSize(minimumSize + 1);
319         final ContentCaptureEvent batchDisappearEvent = events.get(minimumSize);
320 
321         if (expectedIds.length == 1) {
322             assertWithMessage("Should have just one deleted id on %s", batchDisappearEvent)
323                     .that(batchDisappearEvent.getIds()).isNull();
324             assertWithMessage("wrong deleted id on %s", batchDisappearEvent)
325                     .that(batchDisappearEvent.getId()).isEqualTo(expectedIds[0]);
326         } else {
327             assertWithMessage("Should not have individual deleted id on %s", batchDisappearEvent)
328                     .that(batchDisappearEvent.getId()).isNull();
329             final List<AutofillId> actualIds = batchDisappearEvent.getIds();
330             assertWithMessage("wrong deleteds id on %s", batchDisappearEvent)
331                     .that(actualIds).containsExactly((Object[]) expectedIds);
332         }
333         return true;
334     }
335 
336     /**
337      * Asserts the contents of a {@link #TYPE_VIEW_APPEARED} event, without checking for parent
338      */
assertViewWithUnknownParentAppeared( @onNull List<ContentCaptureEvent> events, int index, @NonNull ContentCaptureSessionId expectedSessionId, @NonNull View expectedView)339     public static void assertViewWithUnknownParentAppeared(
340             @NonNull List<ContentCaptureEvent> events, int index,
341             @NonNull ContentCaptureSessionId expectedSessionId, @NonNull View expectedView) {
342         assertViewWithUnknownParentAppeared(events, index, expectedView);
343         assertSessionId(expectedSessionId, expectedView);
344     }
345 
346     /**
347      * Asserts the contents of a {@link #TYPE_VIEW_DISAPPEARED} event for a single view.
348      */
assertViewDisappeared(@onNull List<ContentCaptureEvent> events, int index, @NonNull AutofillId expectedId)349     public static void assertViewDisappeared(@NonNull List<ContentCaptureEvent> events, int index,
350             @NonNull AutofillId expectedId) {
351         final ContentCaptureEvent event = assertCommonViewDisappearedProperties(events, index);
352         assertWithMessage("wrong autofillId on event %s (index %s)", event, index)
353             .that(event.getId()).isEqualTo(expectedId);
354         assertWithMessage("event %s (index %s) should not have autofillIds", event, index)
355             .that(event.getIds()).isNull();
356     }
357 
358     /**
359      * Asserts the contents of a {@link #TYPE_VIEW_DISAPPEARED} event for multiple views.
360      */
assertViewsDisappeared(@onNull List<ContentCaptureEvent> events, int index, @NonNull AutofillId... expectedIds)361     public static void assertViewsDisappeared(@NonNull List<ContentCaptureEvent> events, int index,
362             @NonNull AutofillId... expectedIds) {
363         final ContentCaptureEvent event = assertCommonViewDisappearedProperties(events, index);
364         final List<AutofillId> ids = event.getIds();
365         assertWithMessage("no autofillIds on event %s (index %s)", event, index).that(ids)
366                 .isNotNull();
367         assertWithMessage("wrong autofillId on event %s (index %s)", event, index)
368             .that(ids).containsExactly((Object[]) expectedIds).inOrder();
369         assertWithMessage("event %s (index %s) should not have autofillId", event, index)
370             .that(event.getId()).isNull();
371     }
372 
assertCommonViewDisappearedProperties( @onNull List<ContentCaptureEvent> events, int index)373     private static ContentCaptureEvent assertCommonViewDisappearedProperties(
374             @NonNull List<ContentCaptureEvent> events, int index) {
375         final ContentCaptureEvent event = getEvent(events, index, TYPE_VIEW_DISAPPEARED);
376         assertWithMessage("event %s (index %s) should not have a ViewNode", event, index)
377                 .that(event.getViewNode()).isNull();
378         assertWithMessage("event %s (index %s) should not have text", event, index)
379                 .that(event.getText()).isNull();
380         return event;
381     }
382 
383     /**
384      * Asserts the contents of a {@link #TYPE_VIEW_APPEARED} event for a virtual node.
385      */
assertVirtualViewAppeared(@onNull List<ContentCaptureEvent> events, int index, @NonNull ContentCaptureSession session, @NonNull AutofillId parentId, int childId, @Nullable String expectedText)386     public static void assertVirtualViewAppeared(@NonNull List<ContentCaptureEvent> events,
387             int index, @NonNull ContentCaptureSession session, @NonNull AutofillId parentId,
388             int childId, @Nullable String expectedText) {
389         final ContentCaptureEvent event = getEvent(events, index, TYPE_VIEW_APPEARED);
390         final ViewNode node = event.getViewNode();
391         assertThat(node).isNotNull();
392         final AutofillId expectedId = session.newAutofillId(parentId, childId);
393         assertWithMessage("wrong autofill id on %s (index %s)", event, index)
394             .that(node.getAutofillId()).isEqualTo(expectedId);
395         if (expectedText != null) {
396             assertWithMessage("wrong text on %s(index %s) ", event, index)
397                 .that(node.getText().toString()).isEqualTo(expectedText);
398         } else {
399             assertWithMessage("%s (index %s) should not have text", node, index)
400                 .that(node.getText()).isNull();
401         }
402     }
403 
404     /**
405      * Asserts the contents of a {@link #TYPE_VIEW_DISAPPEARED} event for a virtual node.
406      */
assertVirtualViewDisappeared(@onNull List<ContentCaptureEvent> events, int index, @NonNull AutofillId parentId, @NonNull ContentCaptureSession session, long childId)407     public static void assertVirtualViewDisappeared(@NonNull List<ContentCaptureEvent> events,
408             int index, @NonNull AutofillId parentId, @NonNull ContentCaptureSession session,
409             long childId) {
410         assertViewDisappeared(events, index, session.newAutofillId(parentId, childId));
411     }
412 
413     /**
414      * Asserts the contents of a {@link #TYPE_VIEW_DISAPPEARED} event for many virtual nodes.
415      */
assertVirtualViewsDisappeared(@onNull List<ContentCaptureEvent> events, int index, @NonNull AutofillId parentId, @NonNull ContentCaptureSession session, long... childrenIds)416     public static void assertVirtualViewsDisappeared(@NonNull List<ContentCaptureEvent> events,
417             int index, @NonNull AutofillId parentId, @NonNull ContentCaptureSession session,
418             long... childrenIds) {
419         final int size = childrenIds.length;
420         final AutofillId[] expectedIds = new AutofillId[size];
421         for (int i = 0; i < childrenIds.length; i++) {
422             expectedIds[i] = session.newAutofillId(parentId, childrenIds[i]);
423         }
424         assertViewsDisappeared(events, index, expectedIds);
425     }
426 
427     /**
428      * Asserts a view has the given session id.
429      */
assertSessionId(@onNull ContentCaptureSessionId expectedSessionId, @NonNull View view)430     public static void assertSessionId(@NonNull ContentCaptureSessionId expectedSessionId,
431             @NonNull View view) {
432         assertThat(expectedSessionId).isNotNull();
433         final ContentCaptureSession session = view.getContentCaptureSession();
434         assertWithMessage("no session for view %s", view).that(session).isNotNull();
435         assertWithMessage("wrong session id for for view %s", view)
436                 .that(session.getContentCaptureSessionId()).isEqualTo(expectedSessionId);
437     }
438 
439     /**
440      * Asserts the contents of a {@link #TYPE_VIEW_TEXT_CHANGED} event.
441      */
assertViewTextChanged(@onNull List<ContentCaptureEvent> events, int index, @NonNull AutofillId expectedId, @NonNull String expectedText)442     public static void assertViewTextChanged(@NonNull List<ContentCaptureEvent> events, int index,
443             @NonNull AutofillId expectedId, @NonNull String expectedText) {
444         final ContentCaptureEvent event = getEvent(events, index, TYPE_VIEW_TEXT_CHANGED);
445         assertWithMessage("Wrong id on %s (%s)", event, index).that(event.getId())
446                 .isEqualTo(expectedId);
447         assertWithMessage("Wrong text on %s (%s)", event, index).that(event.getText().toString())
448                 .isEqualTo(expectedText);
449     }
450 
451     /**
452      * Asserts the basic contents of a {@link #TYPE_CONTEXT_UPDATED} event.
453      */
assertContextUpdated( @onNull List<ContentCaptureEvent> events, int index)454     public static ContentCaptureEvent assertContextUpdated(
455             @NonNull List<ContentCaptureEvent> events, int index) {
456         final ContentCaptureEvent event = getEvent(events, index, TYPE_CONTEXT_UPDATED);
457         assertWithMessage("event %s (index %s) should not have a ViewNode", event, index)
458                 .that(event.getViewNode()).isNull();
459         assertWithMessage("event %s (index %s) should not have text", event, index)
460                 .that(event.getViewNode()).isNull();
461         assertWithMessage("event %s (index %s) should not have an autofillId", event, index)
462                 .that(event.getId()).isNull();
463         assertWithMessage("event %s (index %s) should not have autofillIds", event, index)
464                 .that(event.getIds()).isNull();
465         return event;
466     }
467 
468     /**
469      * Asserts the order a session was created or destroyed.
470      */
assertLifecycleOrder(int expectedOrder, @NonNull Session session, @NonNull LifecycleOrder type)471     public static void assertLifecycleOrder(int expectedOrder, @NonNull Session session,
472             @NonNull LifecycleOrder type) {
473         switch(type) {
474             case CREATION:
475                 assertWithMessage("Wrong order of creation for session %s", session)
476                     .that(session.creationOrder).isEqualTo(expectedOrder);
477                 break;
478             case DESTRUCTION:
479                 assertWithMessage("Wrong order of destruction for session %s", session)
480                     .that(session.destructionOrder).isEqualTo(expectedOrder);
481                 break;
482             default:
483                 throw new IllegalArgumentException("Invalid type: " + type);
484         }
485     }
486 
487     /**
488      * Gets the event at the given index, failing with the user-friendly message if necessary...
489      */
490     @NonNull
getEvent(@onNull List<ContentCaptureEvent> events, int index, int expectedType)491     private static ContentCaptureEvent getEvent(@NonNull List<ContentCaptureEvent> events,
492             int index, int expectedType) {
493         assertWithMessage("events is null").that(events).isNotNull();
494         final ContentCaptureEvent event = events.get(index);
495         assertWithMessage("no event at index %s (size %s): %s", index, events.size(), events)
496                 .that(event).isNotNull();
497         final int actualType = event.getType();
498         if (actualType != expectedType) {
499             throw new AssertionError(String.format(
500                     "wrong event type (expected %s, actual is %s) at index %s: %s",
501                     eventTypeAsString(expectedType), eventTypeAsString(actualType), index, event));
502         }
503         assertWithMessage("invalid time on %s (index %s)", event, index).that(event.getEventTime())
504                  .isAtLeast(MY_EPOCH);
505         return event;
506     }
507 
508     /**
509      * Gets an user-friendly description of the given event type.
510      */
511     @NonNull
eventTypeAsString(int type)512     public static String eventTypeAsString(int type) {
513         final String string;
514         switch (type) {
515             case TYPE_VIEW_APPEARED:
516                 string = "APPEAR";
517                 break;
518             case TYPE_VIEW_DISAPPEARED:
519                 string = "DISAPPEAR";
520                 break;
521             case TYPE_VIEW_TEXT_CHANGED:
522                 string = "TEXT_CHANGE";
523                 break;
524             case TYPE_VIEW_TREE_APPEARING:
525                 string = "TREE_START";
526                 break;
527             case TYPE_VIEW_TREE_APPEARED:
528                 string = "TREE_END";
529                 break;
530             case TYPE_SESSION_PAUSED:
531                 string = "PAUSED";
532                 break;
533             case TYPE_SESSION_RESUMED:
534                 string = "RESUMED";
535                 break;
536             default:
537                 return "UNKNOWN-" + type;
538         }
539         return String.format("%s-%d", string, type);
540     }
541 
Assertions()542     private Assertions() {
543         throw new UnsupportedOperationException("contain static methods only");
544     }
545 
546     public enum LifecycleOrder {
547         CREATION, DESTRUCTION
548     }
549 }
550