1 /*
2  * Copyright (C) 2017 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 
17 package android.autofillservice.cts;
18 
19 import static android.autofillservice.cts.Helper.CONNECTION_TIMEOUT_MS;
20 
21 import static com.google.common.truth.Truth.assertWithMessage;
22 
23 import android.view.View;
24 import android.view.autofill.AutofillManager.AutofillCallback;
25 
26 import java.util.concurrent.BlockingQueue;
27 import java.util.concurrent.LinkedBlockingQueue;
28 import java.util.concurrent.TimeUnit;
29 
30 /**
31  * Custom {@link AutofillCallback} used to recover events during tests.
32  */
33 final class MyAutofillCallback extends AutofillCallback {
34 
35     private final BlockingQueue<MyEvent> mEvents = new LinkedBlockingQueue<>();
36 
37     @Override
onAutofillEvent(View view, int event)38     public void onAutofillEvent(View view, int event) {
39         mEvents.offer(new MyEvent(view, event));
40     }
41 
42     @Override
onAutofillEvent(View view, int childId, int event)43     public void onAutofillEvent(View view, int childId, int event) {
44         mEvents.offer(new MyEvent(view, childId, event));
45     }
46 
47     /**
48      * Gets the next available event or fail if it times out.
49      */
getEvent()50     MyEvent getEvent() throws InterruptedException {
51         final MyEvent event = mEvents.poll(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
52         if (event == null) {
53             throw new RetryableException("no event in %d ms", CONNECTION_TIMEOUT_MS);
54         }
55         return event;
56     }
57 
58     /**
59      * Used to assert there is no event left behind.
60      */
assertNumberUnhandledEvents(int expected)61     void assertNumberUnhandledEvents(int expected) {
62         assertWithMessage("Invalid number of events left").that(mEvents.size()).isEqualTo(expected);
63     }
64 
65     /**
66      * Convenience method to assert an UI shown event for the given view was received.
67      */
assertUiShownEvent(View expectedView)68     MyEvent assertUiShownEvent(View expectedView) throws InterruptedException {
69         final MyEvent event = getEvent();
70         assertWithMessage("Invalid type on event %s", event).that(event.event)
71                 .isEqualTo(EVENT_INPUT_SHOWN);
72         assertWithMessage("Invalid view on event %s", event).that(event.view)
73             .isSameAs(expectedView);
74         return event;
75     }
76 
77     /**
78      * Convenience method to assert an UI shown event for the given virtual view was received.
79      */
assertUiShownEvent(View expectedView, int expectedChildId)80     void assertUiShownEvent(View expectedView, int expectedChildId) throws InterruptedException {
81         final MyEvent event = assertUiShownEvent(expectedView);
82         assertWithMessage("Invalid child on event %s", event).that(event.childId)
83             .isSameAs(expectedChildId);
84     }
85 
86     /**
87      * Convenience method to assert an UI hidden event for the given view was received.
88      */
assertUiHiddenEvent(View expectedView)89     MyEvent assertUiHiddenEvent(View expectedView) throws InterruptedException {
90         final MyEvent event = getEvent();
91         assertWithMessage("Invalid type on event %s", event).that(event.event)
92                 .isEqualTo(EVENT_INPUT_HIDDEN);
93         assertWithMessage("Invalid view on event %s", event).that(event.view)
94                 .isSameAs(expectedView);
95         return event;
96     }
97 
98     /**
99      * Convenience method to assert an UI hidden event for the given view was received.
100      */
assertUiHiddenEvent(View expectedView, int expectedChildId)101     void assertUiHiddenEvent(View expectedView, int expectedChildId) throws InterruptedException {
102         final MyEvent event = assertUiHiddenEvent(expectedView);
103         assertWithMessage("Invalid child on event %s", event).that(event.childId)
104                 .isSameAs(expectedChildId);
105     }
106 
107     /**
108      * Convenience method to assert an UI unavailable event for the given view was received.
109      */
assertUiUnavailableEvent(View expectedView)110     MyEvent assertUiUnavailableEvent(View expectedView) throws InterruptedException {
111         final MyEvent event = getEvent();
112         assertWithMessage("Invalid type on event %s", event).that(event.event)
113                 .isEqualTo(EVENT_INPUT_UNAVAILABLE);
114         assertWithMessage("Invalid view on event %s", event).that(event.view)
115                 .isSameAs(expectedView);
116         return event;
117     }
118 
119     /**
120      * Convenience method to assert an UI unavailable event for the given view was received.
121      */
assertUiUnavailableEvent(View expectedView, int expectedChildId)122     void assertUiUnavailableEvent(View expectedView, int expectedChildId)
123             throws InterruptedException {
124         final MyEvent event = assertUiUnavailableEvent(expectedView);
125         assertWithMessage("Invalid child on event %s", event).that(event.childId)
126                 .isSameAs(expectedChildId);
127     }
128 
129     private static final class MyEvent {
130         public final View view;
131         public final int childId;
132         public final int event;
133 
MyEvent(View view, int event)134         MyEvent(View view, int event) {
135             this(view, View.NO_ID, event);
136         }
137 
MyEvent(View view, int childId, int event)138         MyEvent(View view, int childId, int event) {
139             this.view = view;
140             this.childId = childId;
141             this.event = event;
142         }
143 
144         @Override
toString()145         public String toString() {
146             return event + ": " + view + " (childId: " + childId + ")";
147         }
148     }
149 }
150