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