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