• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.cts;
18 
19 import static org.mockito.Matchers.any;
20 import static org.mockito.Matchers.argThat;
21 import static org.mockito.Matchers.eq;
22 import static org.mockito.Mockito.doAnswer;
23 import static org.mockito.Mockito.doCallRealMethod;
24 import static org.mockito.Mockito.mock;
25 import static org.mockito.Mockito.when;
26 
27 import android.content.Context;
28 import android.content.res.Configuration;
29 import android.content.res.Resources;
30 import android.graphics.pdf.PdfDocument;
31 import android.os.Bundle;
32 import android.os.CancellationSignal;
33 import android.os.ParcelFileDescriptor;
34 import android.os.SystemClock;
35 import android.print.PageRange;
36 import android.print.PrintAttributes;
37 import android.print.PrintDocumentAdapter;
38 import android.print.PrintDocumentAdapter.LayoutResultCallback;
39 import android.print.PrintDocumentAdapter.WriteResultCallback;
40 import android.print.PrintManager;
41 import android.print.PrinterId;
42 import android.print.cts.services.FirstPrintService;
43 import android.print.cts.services.PrintServiceCallbacks;
44 import android.print.cts.services.PrinterDiscoverySessionCallbacks;
45 import android.print.cts.services.SecondPrintService;
46 import android.print.cts.services.StubbablePrinterDiscoverySession;
47 import android.print.pdf.PrintedPdfDocument;
48 import android.printservice.PrintJob;
49 import android.printservice.PrintService;
50 import android.support.test.uiautomator.UiAutomatorTestCase;
51 import android.support.test.uiautomator.UiObject;
52 import android.support.test.uiautomator.UiObjectNotFoundException;
53 import android.support.test.uiautomator.UiSelector;
54 import android.util.DisplayMetrics;
55 
56 import org.hamcrest.BaseMatcher;
57 import org.hamcrest.Description;
58 import org.mockito.InOrder;
59 import org.mockito.stubbing.Answer;
60 
61 import java.io.File;
62 import java.io.FileOutputStream;
63 import java.io.IOException;
64 import java.util.List;
65 import java.util.Locale;
66 import java.util.concurrent.TimeoutException;
67 
68 /**
69  * This is the base class for print tests.
70  */
71 public abstract class BasePrintTest extends UiAutomatorTestCase {
72 
73     private static final long OPERATION_TIMEOUT = 100000000;
74 
75     private static final String ARG_PRIVILEGED_OPS = "ARG_PRIVILEGED_OPS";
76 
77     private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
78 
79     protected static final String PRINT_JOB_NAME = "Test";
80 
81     private PrintDocumentActivity mActivity;
82 
83     private Locale mOldLocale;
84 
85     private CallCounter mCancelOperationCounter;
86     private CallCounter mLayoutCallCounter;
87     private CallCounter mWriteCallCounter;
88     private CallCounter mFinishCallCounter;
89     private CallCounter mPrintJobQueuedCallCounter;
90     private CallCounter mDestroySessionCallCounter;
91 
92     @Override
setUp()93     public void setUp() throws Exception {
94         // Make sure we start with a clean slate.
95         clearPrintSpoolerData();
96 
97         // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
98         // Dexmaker is used by mockito.
99         System.setProperty("dexmaker.dexcache", getInstrumentation()
100                 .getTargetContext().getCacheDir().getPath());
101 
102         // Set to US locale.
103         Resources resources = getInstrumentation().getTargetContext().getResources();
104         Configuration oldConfiguration = resources.getConfiguration();
105         if (!oldConfiguration.locale.equals(Locale.US)) {
106             mOldLocale = oldConfiguration.locale;
107             DisplayMetrics displayMetrics = resources.getDisplayMetrics();
108             Configuration newConfiguration = new Configuration(oldConfiguration);
109             newConfiguration.locale = Locale.US;
110             resources.updateConfiguration(newConfiguration, displayMetrics);
111         }
112 
113         // Initialize the latches.
114         mCancelOperationCounter = new CallCounter();
115         mLayoutCallCounter = new CallCounter();
116         mFinishCallCounter = new CallCounter();
117         mWriteCallCounter = new CallCounter();
118         mFinishCallCounter = new CallCounter();
119         mPrintJobQueuedCallCounter = new CallCounter();
120         mDestroySessionCallCounter = new CallCounter();
121 
122         // Create the activity for the right locale.
123         createActivity();
124     }
125 
126     @Override
tearDown()127     public void tearDown() throws Exception {
128         // Done with the activity.
129         getActivity().finish();
130 
131         // Restore the locale if needed.
132         if (mOldLocale != null) {
133             Resources resources = getInstrumentation().getTargetContext().getResources();
134             DisplayMetrics displayMetrics = resources.getDisplayMetrics();
135             Configuration newConfiguration = new Configuration(resources.getConfiguration());
136             newConfiguration.locale = mOldLocale;
137             mOldLocale = null;
138             resources.updateConfiguration(newConfiguration, displayMetrics);
139         }
140 
141         // Make sure the spooler is cleaned.
142         clearPrintSpoolerData();
143     }
144 
print(final PrintDocumentAdapter adapter)145     protected void print(final PrintDocumentAdapter adapter) {
146         // Initiate printing as if coming from the app.
147         getInstrumentation().runOnMainSync(new Runnable() {
148             @Override
149             public void run() {
150                 PrintManager printManager = (PrintManager) getActivity()
151                         .getSystemService(Context.PRINT_SERVICE);
152                 printManager.print("Print job", adapter, null);
153             }
154         });
155     }
156 
onCancelOperationCalled()157     protected void onCancelOperationCalled() {
158         mCancelOperationCounter.call();
159     }
160 
onLayoutCalled()161     protected void onLayoutCalled() {
162         mLayoutCallCounter.call();
163     }
164 
getWriteCallCount()165     protected int getWriteCallCount() {
166         return mWriteCallCounter.getCallCount();
167     }
168 
onWriteCalled()169     protected void onWriteCalled() {
170         mWriteCallCounter.call();
171     }
172 
onFinishCalled()173     protected void onFinishCalled() {
174         mFinishCallCounter.call();
175     }
176 
onPrintJobQueuedCalled()177     protected void onPrintJobQueuedCalled() {
178         mPrintJobQueuedCallCounter.call();
179     }
180 
onPrinterDiscoverySessionDestroyCalled()181     protected void onPrinterDiscoverySessionDestroyCalled() {
182         mDestroySessionCallCounter.call();
183     }
184 
waitForCancelOperationCallbackCalled()185     protected void waitForCancelOperationCallbackCalled() {
186         waitForCallbackCallCount(mCancelOperationCounter, 1,
187                 "Did not get expected call to onCancel for the current operation.");
188     }
189 
waitForPrinterDiscoverySessionDestroyCallbackCalled()190     protected void waitForPrinterDiscoverySessionDestroyCallbackCalled() {
191         waitForCallbackCallCount(mDestroySessionCallCounter, 1,
192                 "Did not get expected call to onDestroyPrinterDiscoverySession.");
193     }
194 
waitForServiceOnPrintJobQueuedCallbackCalled()195     protected void waitForServiceOnPrintJobQueuedCallbackCalled() {
196         waitForCallbackCallCount(mPrintJobQueuedCallCounter, 1,
197                 "Did not get expected call to onPrintJobQueued.");
198     }
199 
waitForAdapterFinishCallbackCalled()200     protected void waitForAdapterFinishCallbackCalled() {
201         waitForCallbackCallCount(mFinishCallCounter, 1,
202                 "Did not get expected call to finish.");
203     }
204 
waitForLayoutAdapterCallbackCount(int count)205     protected void waitForLayoutAdapterCallbackCount(int count) {
206         waitForCallbackCallCount(mLayoutCallCounter, count,
207                 "Did not get expected call to layout.");
208     }
209 
waitForWriteAdapterCallback()210     protected void waitForWriteAdapterCallback() {
211         waitForCallbackCallCount(mWriteCallCounter, 1, "Did not get expected call to write.");
212     }
213 
waitForCallbackCallCount(CallCounter counter, int count, String message)214     private void waitForCallbackCallCount(CallCounter counter, int count, String message) {
215         try {
216             counter.waitForCount(count, OPERATION_TIMEOUT);
217         } catch (TimeoutException te) {
218             fail(message);
219         }
220     }
221 
selectPrinter(String printerName)222     protected void selectPrinter(String printerName) throws UiObjectNotFoundException {
223         try {
224             UiObject destinationSpinner = new UiObject(new UiSelector().resourceId(
225                     "com.android.printspooler:id/destination_spinner"));
226             destinationSpinner.click();
227             UiObject printerOption = new UiObject(new UiSelector().text(printerName));
228             printerOption.click();
229         } catch (UiObjectNotFoundException e) {
230             dumpWindowHierarchy();
231             throw new UiObjectNotFoundException(e);
232         }
233     }
234 
changeOrientation(String orientation)235     protected void changeOrientation(String orientation) throws UiObjectNotFoundException {
236         try {
237             UiObject orientationSpinner = new UiObject(new UiSelector().resourceId(
238                     "com.android.printspooler:id/orientation_spinner"));
239             orientationSpinner.click();
240             UiObject orientationOption = new UiObject(new UiSelector().text(orientation));
241             orientationOption.click();
242         } catch (UiObjectNotFoundException e) {
243             dumpWindowHierarchy();
244             throw new UiObjectNotFoundException(e);
245         }
246     }
247 
changeMediaSize(String mediaSize)248     protected void changeMediaSize(String mediaSize) throws UiObjectNotFoundException {
249         try {
250             UiObject mediaSizeSpinner = new UiObject(new UiSelector().resourceId(
251                     "com.android.printspooler:id/paper_size_spinner"));
252             mediaSizeSpinner.click();
253             UiObject mediaSizeOption = new UiObject(new UiSelector().text(mediaSize));
254             mediaSizeOption.click();
255         } catch (UiObjectNotFoundException e) {
256             dumpWindowHierarchy();
257             throw new UiObjectNotFoundException(e);
258         }
259     }
260 
changeColor(String color)261     protected void changeColor(String color) throws UiObjectNotFoundException {
262         try {
263             UiObject colorSpinner = new UiObject(new UiSelector().resourceId(
264                     "com.android.printspooler:id/color_spinner"));
265             colorSpinner.click();
266             UiObject colorOption = new UiObject(new UiSelector().text(color));
267             colorOption.click();
268         } catch (UiObjectNotFoundException e) {
269             dumpWindowHierarchy();
270             throw new UiObjectNotFoundException(e);
271         }
272     }
273 
clickPrintButton()274     protected void clickPrintButton() throws UiObjectNotFoundException {
275         try {
276             UiObject printButton = new UiObject(new UiSelector().resourceId(
277                     "com.android.printspooler:id/print_button"));
278             printButton.click();
279         } catch (UiObjectNotFoundException e) {
280             dumpWindowHierarchy();
281             throw new UiObjectNotFoundException(e);
282         }
283     }
284 
dumpWindowHierarchy()285     private void dumpWindowHierarchy() {
286         String name = "print-test-failure-" + System.currentTimeMillis() + ".xml";
287         File file = new File(getActivity().getFilesDir(), name);
288         getUiDevice().dumpWindowHierarchy(file.toString());
289     }
290 
getActivity()291     protected PrintDocumentActivity getActivity() {
292         return mActivity;
293     }
294 
createActivity()295     private void createActivity() {
296         mActivity = launchActivity(
297                 getInstrumentation().getTargetContext().getPackageName(),
298                 PrintDocumentActivity.class, null);
299     }
300 
openPrintOptions()301     protected void openPrintOptions() throws UiObjectNotFoundException {
302         UiObject expandHandle = new UiObject(new UiSelector().resourceId(
303                 "com.android.printspooler:id/expand_collapse_handle"));
304         expandHandle.click();
305     }
306 
clearPrintSpoolerData()307     protected void clearPrintSpoolerData() throws Exception {
308         IPrivilegedOperations privilegedOps = IPrivilegedOperations.Stub.asInterface(
309                 getParams().getBinder(ARG_PRIVILEGED_OPS));
310         privilegedOps.clearApplicationUserData(PRINT_SPOOLER_PACKAGE_NAME);
311     }
312 
verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock, PrintAttributes oldAttributes, PrintAttributes newAttributes, final boolean forPreview)313     protected void verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock,
314             PrintAttributes oldAttributes, PrintAttributes newAttributes,
315             final boolean forPreview) {
316         inOrder.verify(mock).onLayout(eq(oldAttributes), eq(newAttributes),
317                 any(CancellationSignal.class), any(LayoutResultCallback.class), argThat(
318                         new BaseMatcher<Bundle>() {
319                             @Override
320                             public boolean matches(Object item) {
321                                 Bundle bundle = (Bundle) item;
322                                 return forPreview == bundle.getBoolean(
323                                         PrintDocumentAdapter.EXTRA_PRINT_PREVIEW);
324                             }
325 
326                             @Override
327                             public void describeTo(Description description) {
328                                 /* do nothing */
329                             }
330                         }));
331     }
332 
createMockPrintDocumentAdapter(Answer<Void> layoutAnswer, Answer<Void> writeAnswer, Answer<Void> finishAnswer)333     protected PrintDocumentAdapter createMockPrintDocumentAdapter(Answer<Void> layoutAnswer,
334             Answer<Void> writeAnswer, Answer<Void> finishAnswer) {
335         // Create a mock print adapter.
336         PrintDocumentAdapter adapter = mock(PrintDocumentAdapter.class);
337         if (layoutAnswer != null) {
338             doAnswer(layoutAnswer).when(adapter).onLayout(any(PrintAttributes.class),
339                     any(PrintAttributes.class), any(CancellationSignal.class),
340                     any(LayoutResultCallback.class), any(Bundle.class));
341         }
342         if (writeAnswer != null) {
343             doAnswer(writeAnswer).when(adapter).onWrite(any(PageRange[].class),
344                     any(ParcelFileDescriptor.class), any(CancellationSignal.class),
345                     any(WriteResultCallback.class));
346         }
347         if (finishAnswer != null) {
348             doAnswer(finishAnswer).when(adapter).onFinish();
349         }
350         return adapter;
351     }
352 
353     @SuppressWarnings("unchecked")
createMockPrinterDiscoverySessionCallbacks( Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery, Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking, Answer<Void> onStopPrinterStateTracking, Answer<Void> onDestroy)354     protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
355             Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
356             Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
357             Answer<Void> onStopPrinterStateTracking, Answer<Void> onDestroy) {
358         PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
359 
360         doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
361         when(callbacks.getSession()).thenCallRealMethod();
362 
363         if (onStartPrinterDiscovery != null) {
364             doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
365                     any(List.class));
366         }
367         if (onStopPrinterDiscovery != null) {
368             doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
369         }
370         if (onValidatePrinters != null) {
371             doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
372                     any(List.class));
373         }
374         if (onStartPrinterStateTracking != null) {
375             doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
376                     any(PrinterId.class));
377         }
378         if (onStopPrinterStateTracking != null) {
379             doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
380                     any(PrinterId.class));
381         }
382         if (onDestroy != null) {
383             doAnswer(onDestroy).when(callbacks).onDestroy();
384         }
385 
386         return callbacks;
387     }
388 
createMockPrintServiceCallbacks( Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks, Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob)389     protected PrintServiceCallbacks createMockPrintServiceCallbacks(
390             Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
391             Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
392         final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
393 
394         doCallRealMethod().when(service).setService(any(PrintService.class));
395         when(service.getService()).thenCallRealMethod();
396 
397         if (onCreatePrinterDiscoverySessionCallbacks != null) {
398             doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
399                     .onCreatePrinterDiscoverySessionCallbacks();
400         }
401         if (onPrintJobQueued != null) {
402             doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
403         }
404         if (onRequestCancelPrintJob != null) {
405             doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
406                     any(PrintJob.class));
407         }
408 
409         return service;
410     }
411 
writeBlankPages(PrintAttributes constraints, ParcelFileDescriptor output, int fromIndex, int toIndex)412     protected void writeBlankPages(PrintAttributes constraints, ParcelFileDescriptor output,
413             int fromIndex, int toIndex) throws IOException {
414         PrintedPdfDocument document = new PrintedPdfDocument(getActivity(), constraints);
415         final int pageCount = toIndex - fromIndex + 1;
416         for (int i = 0; i < pageCount; i++) {
417             PdfDocument.Page page = document.startPage(i);
418             document.finishPage(page);
419         }
420         FileOutputStream fos = new FileOutputStream(output.getFileDescriptor());
421         document.writeTo(fos);
422         document.close();
423     }
424 
425     protected final class CallCounter {
426         private final Object mLock = new Object();
427 
428         private int mCallCount;
429 
call()430         public void call() {
431             synchronized (mLock) {
432                 mCallCount++;
433                 mLock.notifyAll();
434             }
435         }
436 
getCallCount()437         public int getCallCount() {
438             synchronized (mLock) {
439                 return mCallCount;
440             }
441         }
442 
waitForCount(int count, long timeoutMillis)443         public void waitForCount(int count, long timeoutMillis) throws TimeoutException {
444             synchronized (mLock) {
445                 final long startTimeMillis = SystemClock.uptimeMillis();
446                 while (mCallCount < count) {
447                     try {
448                         final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
449                         final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
450                         if (remainingTimeMillis <= 0) {
451                             throw new TimeoutException();
452                         }
453                         mLock.wait(timeoutMillis);
454                     } catch (InterruptedException ie) {
455                         /* ignore */
456                     }
457                 }
458             }
459         }
460     }
461 }
462