| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.print; |
| |
| import static org.mockito.Matchers.any; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.doCallRealMethod; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.when; |
| |
| import android.annotation.NonNull; |
| import android.app.Instrumentation; |
| import android.content.Context; |
| import android.content.pm.PackageManager; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.os.CancellationSignal; |
| import android.os.LocaleList; |
| import android.os.ParcelFileDescriptor; |
| import android.os.SystemClock; |
| import android.print.PrintAttributes; |
| import android.print.PrintDocumentAdapter; |
| import android.print.PrintManager; |
| import android.print.PrinterId; |
| import android.print.mockservice.PrintServiceCallbacks; |
| import android.print.mockservice.PrinterDiscoverySessionCallbacks; |
| import android.print.mockservice.StubbablePrinterDiscoverySession; |
| import android.printservice.CustomPrinterIconCallback; |
| import android.printservice.PrintJob; |
| import android.printservice.PrintService; |
| import android.test.InstrumentationTestCase; |
| import android.util.DisplayMetrics; |
| |
| import org.mockito.stubbing.Answer; |
| |
| import java.io.BufferedReader; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.concurrent.TimeoutException; |
| |
| /** |
| * This is the base class for print tests. |
| */ |
| public abstract class BasePrintTest extends InstrumentationTestCase { |
| |
| private static final long OPERATION_TIMEOUT = 30000; |
| private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success"; |
| private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s"; |
| private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable "; |
| private static final String COMMAND_PREFIX_DISABLE_IME = "ime disable "; |
| private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT |
| |
| private PrintTestActivity mActivity; |
| private android.print.PrintJob mPrintJob; |
| |
| private LocaleList mOldLocale; |
| |
| private CallCounter mStartCallCounter; |
| private CallCounter mStartSessionCallCounter; |
| |
| private String[] mEnabledImes; |
| |
| private String[] getEnabledImes() throws IOException { |
| List<String> imeList = new ArrayList<>(); |
| |
| ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation() |
| .executeShellCommand(COMMAND_LIST_ENABLED_IME_COMPONENTS); |
| try (BufferedReader reader = new BufferedReader( |
| new InputStreamReader(new FileInputStream(pfd.getFileDescriptor())))) { |
| |
| String line; |
| while ((line = reader.readLine()) != null) { |
| imeList.add(line); |
| } |
| } |
| |
| String[] imeArray = new String[imeList.size()]; |
| imeList.toArray(imeArray); |
| |
| return imeArray; |
| } |
| |
| private void disableImes() throws Exception { |
| mEnabledImes = getEnabledImes(); |
| for (String ime : mEnabledImes) { |
| String disableImeCommand = COMMAND_PREFIX_DISABLE_IME + ime; |
| runShellCommand(getInstrumentation(), disableImeCommand); |
| } |
| } |
| |
| private void enableImes() throws Exception { |
| for (String ime : mEnabledImes) { |
| String enableImeCommand = COMMAND_PREFIX_ENABLE_IME + ime; |
| runShellCommand(getInstrumentation(), enableImeCommand); |
| } |
| mEnabledImes = null; |
| } |
| |
| @Override |
| protected void runTest() throws Throwable { |
| // Do nothing if the device does not support printing. |
| if (supportsPrinting()) { |
| super.runTest(); |
| } |
| } |
| |
| @Override |
| public void setUp() throws Exception { |
| super.setUp(); |
| if (!supportsPrinting()) { |
| return; |
| } |
| |
| // Make sure we start with a clean slate. |
| clearPrintSpoolerData(); |
| disableImes(); |
| |
| // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2 |
| // Dexmaker is used by mockito. |
| System.setProperty("dexmaker.dexcache", getInstrumentation() |
| .getTargetContext().getCacheDir().getPath()); |
| |
| // Set to US locale. |
| Resources resources = getInstrumentation().getTargetContext().getResources(); |
| Configuration oldConfiguration = resources.getConfiguration(); |
| if (!oldConfiguration.getLocales().get(0).equals(Locale.US)) { |
| mOldLocale = oldConfiguration.getLocales(); |
| DisplayMetrics displayMetrics = resources.getDisplayMetrics(); |
| Configuration newConfiguration = new Configuration(oldConfiguration); |
| newConfiguration.setLocale(Locale.US); |
| resources.updateConfiguration(newConfiguration, displayMetrics); |
| } |
| |
| // Initialize the latches. |
| mStartCallCounter = new CallCounter(); |
| mStartSessionCallCounter = new CallCounter(); |
| |
| // Create the activity for the right locale. |
| createActivity(); |
| } |
| |
| @Override |
| public void tearDown() throws Exception { |
| if (!supportsPrinting()) { |
| return; |
| } |
| |
| // Done with the activity. |
| getActivity().finish(); |
| enableImes(); |
| |
| // Restore the locale if needed. |
| if (mOldLocale != null) { |
| Resources resources = getInstrumentation().getTargetContext().getResources(); |
| DisplayMetrics displayMetrics = resources.getDisplayMetrics(); |
| Configuration newConfiguration = new Configuration(resources.getConfiguration()); |
| newConfiguration.setLocales(mOldLocale); |
| mOldLocale = null; |
| resources.updateConfiguration(newConfiguration, displayMetrics); |
| } |
| |
| // Make sure the spooler is cleaned, this also un-approves all services |
| clearPrintSpoolerData(); |
| |
| super.tearDown(); |
| } |
| |
| protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter, |
| final PrintAttributes attributes) { |
| // Initiate printing as if coming from the app. |
| getInstrumentation().runOnMainSync(new Runnable() { |
| @Override |
| public void run() { |
| PrintManager printManager = (PrintManager) getActivity() |
| .getSystemService(Context.PRINT_SERVICE); |
| mPrintJob = printManager.print("Print job", adapter, attributes); |
| } |
| }); |
| |
| return mPrintJob; |
| } |
| |
| protected void onStartCalled() { |
| mStartCallCounter.call(); |
| } |
| |
| protected void onPrinterDiscoverySessionStartCalled() { |
| mStartSessionCallCounter.call(); |
| } |
| |
| protected void waitForPrinterDiscoverySessionStartCallbackCalled() { |
| waitForCallbackCallCount(mStartSessionCallCounter, 1, |
| "Did not get expected call to onStartPrinterDiscoverySession."); |
| } |
| |
| protected void waitForStartAdapterCallbackCalled() { |
| waitForCallbackCallCount(mStartCallCounter, 1, "Did not get expected call to start."); |
| } |
| |
| private void waitForCallbackCallCount(CallCounter counter, int count, String message) { |
| try { |
| counter.waitForCount(count, OPERATION_TIMEOUT); |
| } catch (TimeoutException te) { |
| fail(message); |
| } |
| } |
| |
| protected PrintTestActivity getActivity() { |
| return mActivity; |
| } |
| |
| protected void createActivity() { |
| mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(), |
| PrintTestActivity.class, null); |
| } |
| |
| public static String runShellCommand(Instrumentation instrumentation, String cmd) |
| throws IOException { |
| ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd); |
| byte[] buf = new byte[512]; |
| int bytesRead; |
| FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); |
| StringBuffer stdout = new StringBuffer(); |
| while ((bytesRead = fis.read(buf)) != -1) { |
| stdout.append(new String(buf, 0, bytesRead)); |
| } |
| fis.close(); |
| return stdout.toString(); |
| } |
| |
| protected void clearPrintSpoolerData() throws Exception { |
| assertTrue("failed to clear print spooler data", |
| runShellCommand(getInstrumentation(), String.format( |
| "pm clear --user %d %s", CURRENT_USER_ID, |
| PrintManager.PRINT_SPOOLER_PACKAGE_NAME)) |
| .contains(PM_CLEAR_SUCCESS_OUTPUT)); |
| } |
| |
| @SuppressWarnings("unchecked") |
| protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks( |
| Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery, |
| Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking, |
| Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking, |
| Answer<Void> onDestroy) { |
| PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class); |
| |
| doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class)); |
| when(callbacks.getSession()).thenCallRealMethod(); |
| |
| if (onStartPrinterDiscovery != null) { |
| doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery( |
| any(List.class)); |
| } |
| if (onStopPrinterDiscovery != null) { |
| doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery(); |
| } |
| if (onValidatePrinters != null) { |
| doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters( |
| any(List.class)); |
| } |
| if (onStartPrinterStateTracking != null) { |
| doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking( |
| any(PrinterId.class)); |
| } |
| if (onRequestCustomPrinterIcon != null) { |
| doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon( |
| any(PrinterId.class), any(CancellationSignal.class), |
| any(CustomPrinterIconCallback.class)); |
| } |
| if (onStopPrinterStateTracking != null) { |
| doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking( |
| any(PrinterId.class)); |
| } |
| if (onDestroy != null) { |
| doAnswer(onDestroy).when(callbacks).onDestroy(); |
| } |
| |
| return callbacks; |
| } |
| |
| protected PrintServiceCallbacks createMockPrintServiceCallbacks( |
| Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks, |
| Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) { |
| final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class); |
| |
| doCallRealMethod().when(service).setService(any(PrintService.class)); |
| when(service.getService()).thenCallRealMethod(); |
| |
| if (onCreatePrinterDiscoverySessionCallbacks != null) { |
| doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service) |
| .onCreatePrinterDiscoverySessionCallbacks(); |
| } |
| if (onPrintJobQueued != null) { |
| doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class)); |
| } |
| if (onRequestCancelPrintJob != null) { |
| doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob( |
| any(PrintJob.class)); |
| } |
| |
| return service; |
| } |
| |
| protected final class CallCounter { |
| private final Object mLock = new Object(); |
| |
| private int mCallCount; |
| |
| public void call() { |
| synchronized (mLock) { |
| mCallCount++; |
| mLock.notifyAll(); |
| } |
| } |
| |
| public int getCallCount() { |
| synchronized (mLock) { |
| return mCallCount; |
| } |
| } |
| |
| public void waitForCount(int count, long timeoutMillis) throws TimeoutException { |
| synchronized (mLock) { |
| final long startTimeMillis = SystemClock.uptimeMillis(); |
| while (mCallCount < count) { |
| try { |
| final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; |
| final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis; |
| if (remainingTimeMillis <= 0) { |
| throw new TimeoutException(); |
| } |
| mLock.wait(timeoutMillis); |
| } catch (InterruptedException ie) { |
| /* ignore */ |
| } |
| } |
| } |
| } |
| } |
| |
| protected boolean supportsPrinting() { |
| return getInstrumentation().getContext().getPackageManager() |
| .hasSystemFeature(PackageManager.FEATURE_PRINTING); |
| } |
| } |