• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.print;
18 
19 import static org.junit.Assert.assertTrue;
20 import static org.junit.Assert.fail;
21 import static org.junit.Assume.assumeTrue;
22 import static org.mockito.Matchers.any;
23 import static org.mockito.Mockito.doAnswer;
24 import static org.mockito.Mockito.doCallRealMethod;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.when;
27 
28 import android.annotation.NonNull;
29 import android.app.Instrumentation;
30 import android.content.Context;
31 import android.content.pm.PackageManager;
32 import android.os.CancellationSignal;
33 import android.os.ParcelFileDescriptor;
34 import android.os.SystemClock;
35 import android.print.mockservice.PrintServiceCallbacks;
36 import android.print.mockservice.PrinterDiscoverySessionCallbacks;
37 import android.print.mockservice.StubbablePrinterDiscoverySession;
38 import android.printservice.CustomPrinterIconCallback;
39 import android.printservice.PrintJob;
40 import android.printservice.PrintService;
41 import android.support.test.InstrumentationRegistry;
42 import android.support.test.uiautomator.UiDevice;
43 import android.support.test.rule.ActivityTestRule;
44 
45 import org.junit.After;
46 import org.junit.Before;
47 import org.junit.BeforeClass;
48 import org.junit.Rule;
49 import org.mockito.stubbing.Answer;
50 
51 import java.io.FileInputStream;
52 import java.io.IOException;
53 import java.util.List;
54 import java.util.concurrent.TimeoutException;
55 
56 /**
57  * This is the base class for print tests.
58  */
59 abstract class BasePrintTest {
60     protected static final long OPERATION_TIMEOUT = 30000;
61     private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
62     private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
63 
64     private android.print.PrintJob mPrintJob;
65 
66     private CallCounter mStartCallCounter;
67     private CallCounter mStartSessionCallCounter;
68 
69     private static Instrumentation sInstrumentation;
70     private static UiDevice sUiDevice;
71 
72     @Rule
73     public ActivityTestRule<PrintTestActivity> mActivityRule =
74             new ActivityTestRule<>(PrintTestActivity.class, false, true);
75 
76     /**
77      * {@link Runnable} that can throw and {@link Exception}
78      */
79     interface Invokable {
80         /**
81          * Execute the invokable
82          *
83          * @throws Exception
84          */
run()85         void run() throws Exception;
86     }
87 
88     /**
89      * Assert that the invokable throws an expectedException
90      *
91      * @param invokable The {@link Invokable} to run
92      * @param expectedClass The {@link Exception} that is supposed to be thrown
93      */
assertException(Invokable invokable, Class<? extends Exception> expectedClass)94     void assertException(Invokable invokable, Class<? extends Exception> expectedClass)
95             throws Exception {
96         try {
97             invokable.run();
98         } catch (Exception e) {
99             if (e.getClass().isAssignableFrom(expectedClass)) {
100                 return;
101             } else {
102                 throw e;
103             }
104         }
105 
106         throw new AssertionError("No exception thrown");
107     }
108 
109     /**
110      * Return the UI device
111      *
112      * @return the UI device
113      */
getUiDevice()114     public UiDevice getUiDevice() {
115         return sUiDevice;
116     }
117 
getInstrumentation()118     protected static Instrumentation getInstrumentation() {
119         return sInstrumentation;
120     }
121 
122     @BeforeClass
setUpClass()123     public static void setUpClass() throws Exception {
124         sInstrumentation = InstrumentationRegistry.getInstrumentation();
125         assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature(
126                 PackageManager.FEATURE_PRINTING));
127 
128         sUiDevice = UiDevice.getInstance(sInstrumentation);
129 
130         // Make sure we start with a clean slate.
131         clearPrintSpoolerData();
132 
133         // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
134         // Dexmaker is used by mockito.
135         System.setProperty("dexmaker.dexcache", getInstrumentation()
136                 .getTargetContext().getCacheDir().getPath());
137     }
138 
139     @Before
initCounters()140     public void initCounters() throws Exception {
141         // Initialize the latches.
142         mStartCallCounter = new CallCounter();
143         mStartSessionCallCounter = new CallCounter();
144     }
145 
146     @After
exitActivities()147     public void exitActivities() throws Exception {
148         // Exit print spooler
149         getUiDevice().pressBack();
150         getUiDevice().pressBack();
151     }
152 
print(@onNull final PrintDocumentAdapter adapter, final PrintAttributes attributes)153     protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter,
154             final PrintAttributes attributes) {
155         // Initiate printing as if coming from the app.
156         getInstrumentation().runOnMainSync(() -> {
157             PrintManager printManager = (PrintManager) getActivity()
158                     .getSystemService(Context.PRINT_SERVICE);
159             mPrintJob = printManager.print("Print job", adapter, attributes);
160         });
161 
162         return mPrintJob;
163     }
164 
onStartCalled()165     protected void onStartCalled() {
166         mStartCallCounter.call();
167     }
168 
onPrinterDiscoverySessionStartCalled()169     protected void onPrinterDiscoverySessionStartCalled() {
170         mStartSessionCallCounter.call();
171     }
172 
waitForPrinterDiscoverySessionStartCallbackCalled()173     protected void waitForPrinterDiscoverySessionStartCallbackCalled() {
174         waitForCallbackCallCount(mStartSessionCallCounter, 1,
175                 "Did not get expected call to onStartPrinterDiscoverySession.");
176     }
177 
waitForStartAdapterCallbackCalled()178     protected void waitForStartAdapterCallbackCalled() {
179         waitForCallbackCallCount(mStartCallCounter, 1, "Did not get expected call to start.");
180     }
181 
waitForCallbackCallCount(CallCounter counter, int count, String message)182     private static void waitForCallbackCallCount(CallCounter counter, int count, String message) {
183         try {
184             counter.waitForCount(count, OPERATION_TIMEOUT);
185         } catch (TimeoutException te) {
186             fail(message);
187         }
188     }
189 
getActivity()190     protected PrintTestActivity getActivity() {
191         return mActivityRule.getActivity();
192     }
193 
runShellCommand(Instrumentation instrumentation, String cmd)194     public static String runShellCommand(Instrumentation instrumentation, String cmd)
195             throws IOException {
196         ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd);
197         byte[] buf = new byte[512];
198         int bytesRead;
199         FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
200         StringBuilder stdout = new StringBuilder();
201         while ((bytesRead = fis.read(buf)) != -1) {
202             stdout.append(new String(buf, 0, bytesRead));
203         }
204         fis.close();
205         return stdout.toString();
206     }
207 
clearPrintSpoolerData()208     protected static void clearPrintSpoolerData() throws Exception {
209         assertTrue("failed to clear print spooler data",
210                 runShellCommand(getInstrumentation(), String.format(
211                         "pm clear --user %d %s", CURRENT_USER_ID,
212                         PrintManager.PRINT_SPOOLER_PACKAGE_NAME))
213                         .contains(PM_CLEAR_SUCCESS_OUTPUT));
214     }
215 
216     @SuppressWarnings("unchecked")
createMockPrinterDiscoverySessionCallbacks( Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery, Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking, Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking, Answer<Void> onDestroy)217     protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
218             Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
219             Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
220             Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking,
221             Answer<Void> onDestroy) {
222         PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
223 
224         doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
225         when(callbacks.getSession()).thenCallRealMethod();
226 
227         if (onStartPrinterDiscovery != null) {
228             doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
229                     any(List.class));
230         }
231         if (onStopPrinterDiscovery != null) {
232             doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
233         }
234         if (onValidatePrinters != null) {
235             doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
236                     any(List.class));
237         }
238         if (onStartPrinterStateTracking != null) {
239             doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
240                     any(PrinterId.class));
241         }
242         if (onRequestCustomPrinterIcon != null) {
243             doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon(
244                     any(PrinterId.class), any(CancellationSignal.class),
245                     any(CustomPrinterIconCallback.class));
246         }
247         if (onStopPrinterStateTracking != null) {
248             doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
249                     any(PrinterId.class));
250         }
251         if (onDestroy != null) {
252             doAnswer(onDestroy).when(callbacks).onDestroy();
253         }
254 
255         return callbacks;
256     }
257 
createMockPrintServiceCallbacks( Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks, Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob)258     protected PrintServiceCallbacks createMockPrintServiceCallbacks(
259             Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
260             Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
261         final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
262 
263         doCallRealMethod().when(service).setService(any(PrintService.class));
264         when(service.getService()).thenCallRealMethod();
265 
266         if (onCreatePrinterDiscoverySessionCallbacks != null) {
267             doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
268                     .onCreatePrinterDiscoverySessionCallbacks();
269         }
270         if (onPrintJobQueued != null) {
271             doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
272         }
273         if (onRequestCancelPrintJob != null) {
274             doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
275                     any(PrintJob.class));
276         }
277 
278         return service;
279     }
280 
281     private static final class CallCounter {
282         private final Object mLock = new Object();
283 
284         private int mCallCount;
285 
call()286         public void call() {
287             synchronized (mLock) {
288                 mCallCount++;
289                 mLock.notifyAll();
290             }
291         }
292 
getCallCount()293         int getCallCount() {
294             synchronized (mLock) {
295                 return mCallCount;
296             }
297         }
298 
waitForCount(int count, long timeoutMillis)299         public void waitForCount(int count, long timeoutMillis) throws TimeoutException {
300             synchronized (mLock) {
301                 final long startTimeMillis = SystemClock.uptimeMillis();
302                 while (mCallCount < count) {
303                     try {
304                         final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
305                         final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
306                         if (remainingTimeMillis <= 0) {
307                             throw new TimeoutException();
308                         }
309                         mLock.wait(timeoutMillis);
310                     } catch (InterruptedException ie) {
311                         /* ignore */
312                     }
313                 }
314             }
315         }
316     }
317 }
318