• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.CALLBACK_NOT_CALLED_TIMEOUT_MS;
21 import static android.autofillservice.cts.Timeouts.CONNECTION_TIMEOUT;
22 
23 import static com.google.common.truth.Truth.assertWithMessage;
24 
25 import android.os.Handler;
26 import android.os.HandlerThread;
27 import android.util.Log;
28 import android.view.View;
29 import android.view.autofill.AutofillManager.AutofillCallback;
30 
31 import com.android.compatibility.common.util.RetryableException;
32 import com.android.compatibility.common.util.Timeout;
33 
34 import java.util.concurrent.BlockingQueue;
35 import java.util.concurrent.LinkedBlockingQueue;
36 import java.util.concurrent.TimeUnit;
37 
38 /**
39  * Custom {@link AutofillCallback} used to recover events during tests.
40  */
41 public final class MyAutofillCallback extends AutofillCallback {
42 
43     private static final String TAG = "MyAutofillCallback";
44     private final BlockingQueue<MyEvent> mEvents = new LinkedBlockingQueue<>();
45 
46     public static final Timeout MY_TIMEOUT = CONNECTION_TIMEOUT;
47 
48     // We must handle all requests in a separate thread as the service's main thread is the also
49     // the UI thread of the test process and we don't want to hose it in case of failures here
50     private static final HandlerThread sMyThread = new HandlerThread("MyCallbackThread");
51     private final Handler mHandler;
52 
53     static {
Log.i(TAG, "Starting thread " + sMyThread)54         Log.i(TAG, "Starting thread " + sMyThread);
sMyThread.start()55         sMyThread.start();
56     }
57 
MyAutofillCallback()58     MyAutofillCallback() {
59         mHandler = Handler.createAsync(sMyThread.getLooper());
60     }
61 
62     @Override
onAutofillEvent(View view, int event)63     public void onAutofillEvent(View view, int event) {
64         mHandler.post(() -> offer(new MyEvent(view, event)));
65     }
66 
67     @Override
onAutofillEvent(View view, int childId, int event)68     public void onAutofillEvent(View view, int childId, int event) {
69         mHandler.post(() -> offer(new MyEvent(view, childId, event)));
70     }
71 
offer(MyEvent event)72     private void offer(MyEvent event) {
73         Log.v(TAG, "offer: " + event);
74         Helper.offer(mEvents, event, MY_TIMEOUT.ms());
75     }
76 
77     /**
78      * Gets the next available event or fail if it times out.
79      */
getEvent()80     public MyEvent getEvent() throws InterruptedException {
81         final MyEvent event = mEvents.poll(CONNECTION_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
82         if (event == null) {
83             throw new RetryableException(CONNECTION_TIMEOUT, "no event");
84         }
85         return event;
86     }
87 
88     /**
89      * Assert no more events were received.
90      */
assertNotCalled()91     public void assertNotCalled() throws InterruptedException {
92         final MyEvent event = mEvents.poll(CALLBACK_NOT_CALLED_TIMEOUT_MS, TimeUnit.MILLISECONDS);
93         if (event != null) {
94             // Not retryable.
95             throw new IllegalStateException("should not have received " + event);
96         }
97     }
98 
99     /**
100      * Used to assert there is no event left behind.
101      */
assertNumberUnhandledEvents(int expected)102     public void assertNumberUnhandledEvents(int expected) {
103         assertWithMessage("Invalid number of events left: %s", mEvents).that(mEvents.size())
104                 .isEqualTo(expected);
105     }
106 
107     /**
108      * Convenience method to assert an UI shown event for the given view was received.
109      */
assertUiShownEvent(View expectedView)110     public MyEvent assertUiShownEvent(View expectedView) throws InterruptedException {
111         final MyEvent event = getEvent();
112         assertWithMessage("Invalid type on event %s", event).that(event.event)
113                 .isEqualTo(EVENT_INPUT_SHOWN);
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 shown event for the given virtual view was received.
121      */
assertUiShownEvent(View expectedView, int expectedChildId)122     public void assertUiShownEvent(View expectedView, int expectedChildId)
123             throws InterruptedException {
124         final MyEvent event = assertUiShownEvent(expectedView);
125         assertWithMessage("Invalid child on event %s", event).that(event.childId)
126             .isEqualTo(expectedChildId);
127     }
128 
129     /**
130      * Convenience method to assert an UI shown event a virtual view was received.
131      *
132      * @return virtual child id
133      */
assertUiShownEventForVirtualChild(View expectedView)134     public int assertUiShownEventForVirtualChild(View expectedView) throws InterruptedException {
135         final MyEvent event = assertUiShownEvent(expectedView);
136         return event.childId;
137     }
138 
139     /**
140      * Convenience method to assert an UI hidden event for the given view was received.
141      */
assertUiHiddenEvent(View expectedView)142     public MyEvent assertUiHiddenEvent(View expectedView) throws InterruptedException {
143         final MyEvent event = getEvent();
144         assertWithMessage("Invalid type on event %s", event).that(event.event)
145                 .isEqualTo(EVENT_INPUT_HIDDEN);
146         assertWithMessage("Invalid view on event %s", event).that(event.view)
147                 .isSameAs(expectedView);
148         return event;
149     }
150 
151     /**
152      * Convenience method to assert an UI hidden event for the given view was received.
153      */
assertUiHiddenEvent(View expectedView, int expectedChildId)154     public void assertUiHiddenEvent(View expectedView, int expectedChildId)
155             throws InterruptedException {
156         final MyEvent event = assertUiHiddenEvent(expectedView);
157         assertWithMessage("Invalid child on event %s", event).that(event.childId)
158                 .isEqualTo(expectedChildId);
159     }
160 
161     /**
162      * Convenience method to assert an UI unavailable event for the given view was received.
163      */
assertUiUnavailableEvent(View expectedView)164     public MyEvent assertUiUnavailableEvent(View expectedView) throws InterruptedException {
165         final MyEvent event = getEvent();
166         assertWithMessage("Invalid type on event %s", event).that(event.event)
167                 .isEqualTo(EVENT_INPUT_UNAVAILABLE);
168         assertWithMessage("Invalid view on event %s", event).that(event.view)
169                 .isSameAs(expectedView);
170         return event;
171     }
172 
173     /**
174      * Convenience method to assert an UI unavailable event for the given view was received.
175      */
assertUiUnavailableEvent(View expectedView, int expectedChildId)176     public void assertUiUnavailableEvent(View expectedView, int expectedChildId)
177             throws InterruptedException {
178         final MyEvent event = assertUiUnavailableEvent(expectedView);
179         assertWithMessage("Invalid child on event %s", event).that(event.childId)
180                 .isEqualTo(expectedChildId);
181     }
182 
183     private static final class MyEvent {
184         public final View view;
185         public final int childId;
186         public final int event;
187 
MyEvent(View view, int event)188         MyEvent(View view, int event) {
189             this(view, View.NO_ID, event);
190         }
191 
MyEvent(View view, int childId, int event)192         MyEvent(View view, int childId, int event) {
193             this.view = view;
194             this.childId = childId;
195             this.event = event;
196         }
197 
198         @Override
toString()199         public String toString() {
200             return callbackEventAsString(event) + ": " + view + " (childId: " + childId + ")";
201         }
202     }
203 }
204