Home | History | Annotate | Download | only in cts
      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.cts;
     18 
     19 import static android.print.test.Utils.eventually;
     20 
     21 import android.os.ParcelFileDescriptor;
     22 import android.platform.test.annotations.AppModeFull;
     23 import android.print.PageRange;
     24 import android.print.PrintAttributes;
     25 import android.print.PrintAttributes.Margins;
     26 import android.print.PrintAttributes.MediaSize;
     27 import android.print.PrintAttributes.Resolution;
     28 import android.print.PrintDocumentAdapter;
     29 import android.print.PrintDocumentAdapter.LayoutResultCallback;
     30 import android.print.PrintDocumentAdapter.WriteResultCallback;
     31 import android.print.PrintDocumentInfo;
     32 import android.print.PrintJobInfo;
     33 import android.print.PrinterCapabilitiesInfo;
     34 import android.print.PrinterId;
     35 import android.print.PrinterInfo;
     36 import android.print.test.BasePrintTest;
     37 import android.print.test.services.CustomPrintOptionsActivity;
     38 import android.print.test.services.FirstPrintService;
     39 import android.print.test.services.PrintServiceCallbacks;
     40 import android.print.test.services.PrinterDiscoverySessionCallbacks;
     41 import android.print.test.services.SecondPrintService;
     42 import android.print.test.services.StubbablePrinterDiscoverySession;
     43 import android.support.test.runner.AndroidJUnit4;
     44 import android.support.test.uiautomator.By;
     45 import android.support.test.uiautomator.UiObject;
     46 import android.support.test.uiautomator.UiObjectNotFoundException;
     47 import android.support.test.uiautomator.UiSelector;
     48 import android.util.Log;
     49 
     50 import org.junit.Before;
     51 import org.junit.Test;
     52 import org.junit.runner.RunWith;
     53 
     54 import java.util.ArrayList;
     55 import java.util.Arrays;
     56 import java.util.List;
     57 import java.util.concurrent.TimeoutException;
     58 
     59 /**
     60  * This test verifies changes to the printer capabilities are applied correctly.
     61  */
     62 @AppModeFull(reason = "Print UI cannot resolve custom print options activity in a instant app")
     63 @RunWith(AndroidJUnit4.class)
     64 public class CustomPrintOptionsTest extends BasePrintTest {
     65     private final static String LOG_TAG = "CustomPrintOptionsTest";
     66     private static final String PRINTER_NAME = "Test printer";
     67     private static final int MAX_TRIES = 10;
     68 
     69     // Default settings
     70     private final PageRange[] DEFAULT_PAGES = new PageRange[] { new PageRange(0, 0) };
     71     private final MediaSize DEFAULT_MEDIA_SIZE = MediaSize.ISO_A0;
     72     private final int DEFAULT_COLOR_MODE = PrintAttributes.COLOR_MODE_COLOR;
     73     private final int DEFAULT_DUPLEX_MODE = PrintAttributes.DUPLEX_MODE_LONG_EDGE;
     74     private final Resolution DEFAULT_RESOLUTION = new Resolution("300x300", "300x300", 300, 300);
     75     private final Margins DEFAULT_MARGINS = new Margins(0, 0, 0, 0);
     76 
     77     // All settings that are tested
     78     private final PageRange[][] PAGESS = { DEFAULT_PAGES, new PageRange[] { new PageRange(1, 1) },
     79             new PageRange[] { new PageRange(0, 2) }
     80     };
     81     private final MediaSize[] MEDIA_SIZES = { DEFAULT_MEDIA_SIZE, MediaSize.ISO_B0 };
     82     private final Integer[] COLOR_MODES = { DEFAULT_COLOR_MODE,
     83             PrintAttributes.COLOR_MODE_MONOCHROME
     84     };
     85     private final Integer[] DUPLEX_MODES = { DEFAULT_DUPLEX_MODE, PrintAttributes.DUPLEX_MODE_NONE
     86     };
     87     private final Resolution[] RESOLUTIONS = { DEFAULT_RESOLUTION,
     88             new Resolution("600x600", "600x600", 600, 600)
     89     };
     90 
     91     private PrintAttributes mLayoutAttributes;
     92     private PrintDocumentAdapter mAdapter;
     93     private static boolean sHasDefaultPrinterSet;
     94 
     95     /**
     96      * Get the page ranges currently selected as described in the UI.
     97      *
     98      * @return Only page ranges from {@link #PAGESS} are detected correctly.
     99      *
    100      * @throws Exception If something was unexpected
    101      */
    102     private PageRange[] getPages() throws Exception {
    103         if (getUiDevice().hasObject(By.text("All 3"))) {
    104             return PAGESS[2];
    105         }
    106 
    107         try {
    108             UiObject pagesEditText = getUiDevice().findObject(new UiSelector().resourceId(
    109                     "com.android.printspooler:id/page_range_edittext"));
    110 
    111             if (pagesEditText.getText().equals("2")) {
    112                 return PAGESS[1];
    113             }
    114 
    115             if (pagesEditText.getText().equals("1")) {
    116                 return PAGESS[0];
    117             }
    118 
    119             return null;
    120         } catch (UiObjectNotFoundException e) {
    121             dumpWindowHierarchy();
    122             throw e;
    123         }
    124     }
    125 
    126     @Before
    127     public void setUpServicesAndAdapter() {
    128         final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
    129                 createMockPrinterDiscoverySessionCallbacks(invocation -> {
    130                     StubbablePrinterDiscoverySession session =
    131                             ((PrinterDiscoverySessionCallbacks) invocation.getMock())
    132                                     .getSession();
    133                     PrinterId printerId = session.getService().generatePrinterId(PRINTER_NAME);
    134                     List<PrinterInfo> printers = new ArrayList<>(1);
    135                     PrinterCapabilitiesInfo.Builder builder =
    136                             new PrinterCapabilitiesInfo.Builder(printerId);
    137 
    138                     builder.setMinMargins(DEFAULT_MARGINS)
    139                             .setColorModes(COLOR_MODES[0] | COLOR_MODES[1],
    140                                     DEFAULT_COLOR_MODE)
    141                             .setDuplexModes(DUPLEX_MODES[0] | DUPLEX_MODES[1],
    142                                     DEFAULT_DUPLEX_MODE)
    143                             .addMediaSize(DEFAULT_MEDIA_SIZE, true)
    144                             .addMediaSize(MEDIA_SIZES[1], false)
    145                             .addResolution(DEFAULT_RESOLUTION, true)
    146                             .addResolution(RESOLUTIONS[1], false);
    147 
    148                     printers.add(new PrinterInfo.Builder(printerId, PRINTER_NAME,
    149                             PrinterInfo.STATUS_IDLE).setCapabilities(builder.build()).build());
    150 
    151                     session.addPrinters(printers);
    152                     return null;
    153                 }, null, null, null, null, null, invocation -> {
    154                     onPrinterDiscoverySessionDestroyCalled();
    155                     return null;
    156                 });
    157 
    158         mAdapter = createMockPrintDocumentAdapter(
    159                 invocation -> {
    160                     LayoutResultCallback callback = (LayoutResultCallback) invocation
    161                             .getArguments()[3];
    162                     PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
    163                             .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
    164                             .setPageCount(3)
    165                             .build();
    166 
    167                     synchronized (CustomPrintOptionsTest.this) {
    168                         mLayoutAttributes = (PrintAttributes) invocation.getArguments()[1];
    169 
    170                         CustomPrintOptionsTest.this.notifyAll();
    171                     }
    172 
    173                     callback.onLayoutFinished(info, true);
    174                     return null;
    175                 },
    176                 invocation -> {
    177                     Object[] args = invocation.getArguments();
    178                     ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
    179                     WriteResultCallback callback = (WriteResultCallback) args[3];
    180 
    181                     PageRange[] writtenPages = (PageRange[]) args[0];
    182 
    183                     writeBlankPages(mLayoutAttributes, fd, writtenPages[0].getStart(),
    184                             writtenPages[0].getEnd());
    185                     fd.close();
    186 
    187                     callback.onWriteFinished(writtenPages);
    188 
    189                     onWriteCalled();
    190 
    191                     return null;
    192                 }, null);
    193 
    194         // Create the service callbacks for the first print service.
    195         PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
    196                 invocation -> firstSessionCallbacks, null, null);
    197 
    198         // Configure the print services.
    199         FirstPrintService.setCallbacks(firstServiceCallbacks);
    200         SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
    201 
    202         // Set default printer
    203         if (!sHasDefaultPrinterSet) {
    204             // This is the first print test that runs. If this is run after other tests these other
    205             // test can still cause memory pressure and make the printactivity to go through a
    206             // destroy-create cycle. In this case we have to retry to operation.
    207             int tries = 0;
    208             while (true) {
    209                 try {
    210                     resetCounters();
    211                     makeDefaultPrinter(mAdapter, PRINTER_NAME);
    212                     break;
    213                 } catch (Throwable e) {
    214                     if (getActivityDestroyCallbackCallCount() > 0 && tries < MAX_TRIES) {
    215                         Log.e(LOG_TAG, "Activity was destroyed during test, retrying", e);
    216 
    217                         tries++;
    218                         continue;
    219                     }
    220 
    221                     throw new RuntimeException(e);
    222                 }
    223             }
    224 
    225             sHasDefaultPrinterSet = true;
    226         }
    227     }
    228 
    229     /**
    230      * Test that we can switch to a specific set of settings via the custom print options activity
    231      *
    232      * @param copyFromOriginal If the print job info should be copied from the original
    233      * @param numCopies        The copies to print
    234      * @param pages            The page ranges to print
    235      * @param mediaSize        The media size to use
    236      * @param isPortrait       If the mediaSize is portrait
    237      * @param colorMode        The color mode to use
    238      * @param duplexMode       The duplex mode to use
    239      * @param resolution       The resolution to use
    240      *
    241      * @throws Exception If anything is unexpected
    242      */
    243     private void testCase(final boolean copyFromOriginal, final Integer numCopies,
    244             final PageRange[] pages, final MediaSize mediaSize, final boolean isPortrait,
    245             final Integer colorMode, final Integer duplexMode, final Resolution resolution)
    246             throws Throwable {
    247         final PrintAttributes.Builder additionalAttributesBuilder = new PrintAttributes.Builder();
    248         final PrintAttributes.Builder newAttributesBuilder = new PrintAttributes.Builder();
    249 
    250         newAttributesBuilder.setMinMargins(DEFAULT_MARGINS);
    251 
    252         if (mediaSize != null) {
    253             if (isPortrait) {
    254                 additionalAttributesBuilder.setMediaSize(mediaSize.asPortrait());
    255                 newAttributesBuilder.setMediaSize(mediaSize.asPortrait());
    256             } else {
    257                 additionalAttributesBuilder.setMediaSize(mediaSize.asLandscape());
    258                 newAttributesBuilder.setMediaSize(mediaSize.asLandscape());
    259             }
    260         } else {
    261             newAttributesBuilder.setMediaSize(DEFAULT_MEDIA_SIZE);
    262         }
    263 
    264         if (colorMode != null) {
    265             additionalAttributesBuilder.setColorMode(colorMode);
    266             newAttributesBuilder.setColorMode(colorMode);
    267         } else {
    268             newAttributesBuilder.setColorMode(DEFAULT_COLOR_MODE);
    269         }
    270 
    271         if (duplexMode != null) {
    272             additionalAttributesBuilder.setDuplexMode(duplexMode);
    273             newAttributesBuilder.setDuplexMode(duplexMode);
    274         } else {
    275             newAttributesBuilder.setDuplexMode(DEFAULT_DUPLEX_MODE);
    276         }
    277 
    278         if (resolution != null) {
    279             additionalAttributesBuilder.setResolution(resolution);
    280             newAttributesBuilder.setResolution(resolution);
    281         } else {
    282             newAttributesBuilder.setResolution(DEFAULT_RESOLUTION);
    283         }
    284 
    285         CustomPrintOptionsActivity.setCallBack(
    286                 (printJob, printer) -> {
    287                     PrintJobInfo.Builder printJobBuilder;
    288 
    289                     if (copyFromOriginal) {
    290                         printJobBuilder = new PrintJobInfo.Builder(printJob);
    291                     } else {
    292                         printJobBuilder = new PrintJobInfo.Builder(null);
    293                     }
    294 
    295                     if (numCopies != null) {
    296                         printJobBuilder.setCopies(numCopies);
    297                     }
    298 
    299                     if (pages != null) {
    300                         printJobBuilder.setPages(pages);
    301                     }
    302 
    303                     if (mediaSize != null || colorMode != null || duplexMode != null
    304                             || resolution != null) {
    305                         printJobBuilder.setAttributes(additionalAttributesBuilder.build());
    306                     }
    307 
    308                     return printJobBuilder.build();
    309                 });
    310 
    311         // Check that the attributes were send to the print service
    312         PrintAttributes newAttributes = newAttributesBuilder.build();
    313         Log.i(LOG_TAG, "Change to attributes: " + newAttributes + ", copies: " + numCopies +
    314                 ", pages: " + Arrays.toString(pages) + ", copyFromOriginal: " + copyFromOriginal);
    315 
    316         // This is the first print test that runs. If this is run after other tests these other test
    317         // can still cause memory pressure and make the printactivity to go through a destroy-create
    318         // cycle. In this case we have to retry to operation.
    319         int tries = 0;
    320         while (true) {
    321             try {
    322                 resetCounters();
    323 
    324                 // Start printing
    325                 print(mAdapter);
    326 
    327                 // Wait for write.
    328                 waitForWriteAdapterCallback(1);
    329 
    330                 // Open the print options.
    331                 openPrintOptions();
    332 
    333                 // Apply options by executing callback above
    334                 Log.d(LOG_TAG, "Apply changes");
    335                 openCustomPrintOptions();
    336 
    337                 Log.d(LOG_TAG, "Check attributes");
    338                 long endTime = System.currentTimeMillis() + OPERATION_TIMEOUT_MILLIS;
    339                 synchronized (this) {
    340                     while (mLayoutAttributes == null ||
    341                             !mLayoutAttributes.equals(newAttributes)) {
    342                         wait(Math.max(1, endTime - System.currentTimeMillis()));
    343 
    344                         if (endTime < System.currentTimeMillis()) {
    345                             throw new TimeoutException(
    346                                     "Print attributes did not change to " + newAttributes + " in " +
    347                                             OPERATION_TIMEOUT_MILLIS + " ms. Current attributes"
    348                                             + mLayoutAttributes);
    349                         }
    350                     }
    351                 }
    352 
    353                 PageRange[] newPages;
    354 
    355                 if (pages == null) {
    356                     newPages = new PageRange[] { new PageRange(0, 2) };
    357                 } else {
    358                     newPages = pages;
    359                 }
    360 
    361                 Log.d(LOG_TAG, "Check pages");
    362                 eventually(() -> {
    363                     PageRange[] actualPages = getPages();
    364                     if (!Arrays.equals(newPages, actualPages)) {
    365                         throw new AssertionError(
    366                                 "Expected " + Arrays.toString(newPages) + ", actual " +
    367                                         Arrays.toString(actualPages));
    368                     }
    369                 });
    370 
    371                 break;
    372             } catch (Throwable e) {
    373                 if (getActivityDestroyCallbackCallCount() > 0 && tries < MAX_TRIES) {
    374                     Log.e(LOG_TAG, "Activity was destroyed during test, retrying", e);
    375 
    376                     tries++;
    377                     continue;
    378                 }
    379 
    380                 throw e;
    381             }
    382         }
    383 
    384         // Abort printing
    385         getUiDevice().pressBack();
    386         getUiDevice().pressBack();
    387         getUiDevice().pressBack();
    388 
    389         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
    390     }
    391 
    392     @Test
    393     public void changeToChangeEveryThingButPages() throws Throwable {
    394         testCase(false, 2, null, MEDIA_SIZES[1], false, COLOR_MODES[1], DUPLEX_MODES[1],
    395                 RESOLUTIONS[1]);
    396     }
    397 
    398     @Test
    399     public void changeToAttributes() throws Throwable {
    400         testCase(false, null, null, MEDIA_SIZES[1], false, COLOR_MODES[1], DUPLEX_MODES[1],
    401                 RESOLUTIONS[1]);
    402     }
    403 
    404     @Test
    405     public void changeToNonAttributes() throws Throwable {
    406         testCase(false, 2, PAGESS[1], null, true, null, null, null);
    407     }
    408 
    409     @Test
    410     public void changeToAttributesNoCopy() throws Throwable {
    411         testCase(true, null, null, MEDIA_SIZES[1], false, COLOR_MODES[1], DUPLEX_MODES[1],
    412                 RESOLUTIONS[1]);
    413     }
    414 
    415     @Test
    416     public void changeToNonAttributesNoCopy() throws Throwable {
    417         testCase(true, 2, PAGESS[1], null, true, null, null, null);
    418     }
    419 
    420     @Test
    421     public void changeToDefault() throws Throwable {
    422         testCase(false, 1, DEFAULT_PAGES, DEFAULT_MEDIA_SIZE, DEFAULT_MEDIA_SIZE.isPortrait(),
    423                 DEFAULT_COLOR_MODE, DEFAULT_DUPLEX_MODE, DEFAULT_RESOLUTION);
    424     }
    425 
    426     @Test
    427     public void changeToDefaultNoCopy() throws Throwable {
    428         testCase(true, 1, DEFAULT_PAGES, DEFAULT_MEDIA_SIZE, DEFAULT_MEDIA_SIZE.isPortrait(),
    429                 DEFAULT_COLOR_MODE, DEFAULT_DUPLEX_MODE, DEFAULT_RESOLUTION);
    430     }
    431 
    432     @Test
    433     public void changeToNothing() throws Throwable {
    434         testCase(false, null, null, null, true, null, null, null);
    435     }
    436 
    437     @Test
    438     public void testChangeToNothingNoCopy() throws Throwable {
    439         testCase(true, null, null, null, true, null, null, null);
    440     }
    441 
    442     @Test
    443     public void changeToAllPages() throws Throwable {
    444         testCase(false, null, PAGESS[2], null, true, null, null, null);
    445     }
    446 
    447     @Test
    448     public void changeToSomePages() throws Throwable {
    449         testCase(false, null, PAGESS[1], null, true, null, null, null);
    450     }
    451 }
    452