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 android.os.ParcelFileDescriptor; 20 import android.print.PageRange; 21 import android.print.PrintAttributes; 22 import android.print.PrintAttributes.Margins; 23 import android.print.PrintAttributes.MediaSize; 24 import android.print.PrintAttributes.Resolution; 25 import android.print.PrintDocumentAdapter; 26 import android.print.PrintDocumentAdapter.LayoutResultCallback; 27 import android.print.PrintDocumentAdapter.WriteResultCallback; 28 import android.print.PrintDocumentInfo; 29 import android.print.PrinterCapabilitiesInfo; 30 import android.print.PrinterId; 31 import android.print.PrinterInfo; 32 import android.print.cts.services.FirstPrintService; 33 import android.print.cts.services.PrintServiceCallbacks; 34 import android.print.cts.services.PrinterDiscoverySessionCallbacks; 35 import android.print.cts.services.SecondPrintService; 36 import android.print.cts.services.StubbablePrinterDiscoverySession; 37 import android.util.Log; 38 import android.support.test.uiautomator.UiObject; 39 import android.support.test.uiautomator.UiSelector; 40 import org.mockito.invocation.InvocationOnMock; 41 import org.mockito.stubbing.Answer; 42 43 import java.util.ArrayList; 44 import java.util.List; 45 import java.util.concurrent.TimeoutException; 46 import java.util.function.Consumer; 47 import java.util.function.Function; 48 49 /** 50 * This test verifies changes to the printer capabilities are applied correctly. 51 */ 52 public class PrinterCapabilitiesTest extends BasePrintTest { 53 private static final String LOG_TAG = "PrinterCapabilitiesTest"; 54 private static final String PRINTER_NAME = "Test printer"; 55 56 private static final Margins DEFAULT_MARGINS = new Margins(0, 0, 0, 0); 57 private static final PrintAttributes.Resolution RESOLUTION_300 = 58 new PrintAttributes.Resolution("300", "300", 300, 300); 59 private static final PrintAttributes.Resolution RESOLUTION_600 = 60 new PrintAttributes.Resolution("600", "600", 600, 600); 61 62 /** 63 * Generate a new list of printers containing a singer printer with the given media size and 64 * status. The other capabilities are default values. 65 * 66 * @param printerId The id of the printer 67 * @param mediaSize The media size to use 68 * @param status The status of th printer 69 * 70 * @return The list of printers 71 */ generatePrinters(PrinterId printerId, MediaSize mediaSize, int status)72 private List<PrinterInfo> generatePrinters(PrinterId printerId, MediaSize mediaSize, 73 int status) { 74 List<PrinterInfo> printers = new ArrayList<>(1); 75 76 PrinterCapabilitiesInfo cap; 77 78 if (mediaSize != null) { 79 PrinterCapabilitiesInfo.Builder builder = new PrinterCapabilitiesInfo.Builder( 80 printerId); 81 builder.setMinMargins(DEFAULT_MARGINS) 82 .setColorModes(PrintAttributes.COLOR_MODE_COLOR, 83 PrintAttributes.COLOR_MODE_COLOR) 84 .setDuplexModes(PrintAttributes.DUPLEX_MODE_NONE, 85 PrintAttributes.DUPLEX_MODE_NONE) 86 .addMediaSize(mediaSize, true) 87 .addResolution(RESOLUTION_300, true); 88 cap = builder.build(); 89 } else { 90 cap = null; 91 } 92 93 printers.add(new PrinterInfo.Builder(printerId, PRINTER_NAME, status).setCapabilities(cap) 94 .build()); 95 96 return printers; 97 } 98 99 /** 100 * Wait until the print activity requested an update with print attributes matching the media 101 * size. 102 * 103 * @param printAttributes The print attributes container 104 * @param mediaSize The media size to match 105 * 106 * @throws Exception If anything unexpected happened, e.g. the attributes did not change. 107 */ waitForMediaSizeChange(PrintAttributes[] printAttributes, MediaSize mediaSize)108 private void waitForMediaSizeChange(PrintAttributes[] printAttributes, MediaSize mediaSize) 109 throws Exception { 110 synchronized (PrinterCapabilitiesTest.this) { 111 long endTime = System.currentTimeMillis() + OPERATION_TIMEOUT_MILLIS; 112 while (printAttributes[0] == null || 113 !printAttributes[0].getMediaSize().equals(mediaSize)) { 114 wait(Math.max(0, endTime - System.currentTimeMillis())); 115 116 if (endTime < System.currentTimeMillis()) { 117 throw new TimeoutException( 118 "Print attributes did not change to " + mediaSize + " in " + 119 OPERATION_TIMEOUT_MILLIS + " ms. Current attributes" 120 + printAttributes[0]); 121 } 122 } 123 } 124 } 125 126 /** 127 * Change the media size of the capabilities of the printer 128 * 129 * @param session The session used in the test 130 * @param printerId The printer to change 131 * @param mediaSize The new mediaSize to apply 132 * @param isAvailable If the printer should be available or not 133 */ changeCapabilities(final StubbablePrinterDiscoverySession session, final PrinterId printerId, final MediaSize mediaSize, final boolean isAvailable)134 private void changeCapabilities(final StubbablePrinterDiscoverySession session, 135 final PrinterId printerId, final MediaSize mediaSize, final boolean isAvailable) { 136 getInstrumentation().runOnMainSync(new Runnable() { 137 @Override 138 public void run() { 139 session.addPrinters(generatePrinters(printerId, mediaSize, isAvailable ? 140 PrinterInfo.STATUS_IDLE : 141 PrinterInfo.STATUS_UNAVAILABLE)); 142 } 143 }); 144 } 145 146 /** 147 * Wait until the message is shown that indicates that a printer is unavilable. 148 * 149 * @throws Exception If anything was unexpected. 150 */ waitForPrinterUnavailable()151 private void waitForPrinterUnavailable() throws Exception { 152 final String PRINTER_UNAVAILABLE_MSG = "This printer isn't available right now."; 153 154 UiObject message = getUiDevice().findObject(new UiSelector().resourceId( 155 "com.android.printspooler:id/message")); 156 if (!message.getText().equals(PRINTER_UNAVAILABLE_MSG)) { 157 throw new Exception("Wrong message: " + message.getText() + " instead of " + 158 PRINTER_UNAVAILABLE_MSG); 159 } 160 } 161 162 /** 163 * A single test case from {@link #testPrinterCapabilityChange}. Tests a single capability 164 * change. 165 * 166 * @param session The session used in the test 167 * @param printerId The ID of the test printer 168 * @param availBefore If the printer should be available before the change 169 * @param msBefore The media size of the printer before the change 170 * @param availAfter If the printer should be available after the change 171 * @param msAfter The media size of the printer after the change 172 * 173 * @throws Exception If anything is unexpected 174 */ testCase(final StubbablePrinterDiscoverySession[] session, final PrinterId[] printerId, final boolean availBefore, final MediaSize msBefore, final boolean availAfter, final MediaSize msAfter)175 private void testCase(final StubbablePrinterDiscoverySession[] session, 176 final PrinterId[] printerId, final boolean availBefore, final MediaSize msBefore, 177 final boolean availAfter, final MediaSize msAfter) throws Exception { 178 if (!supportsPrinting()) { 179 return; 180 } 181 182 Log.i(LOG_TAG, "Test case: " + availBefore + ", " + msBefore + " -> " + availAfter + ", " 183 + msAfter); 184 185 final PrintAttributes[] layoutAttributes = new PrintAttributes[1]; 186 final PrintAttributes[] writeAttributes = new PrintAttributes[1]; 187 188 PrintDocumentAdapter adapter = createMockPrintDocumentAdapter( 189 new Answer<Void>() { 190 @Override 191 public Void answer(InvocationOnMock invocation) throws Throwable { 192 LayoutResultCallback callback = (LayoutResultCallback) invocation 193 .getArguments()[3]; 194 PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME) 195 .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT) 196 .setPageCount(1) 197 .build(); 198 199 synchronized (PrinterCapabilitiesTest.this) { 200 layoutAttributes[0] = (PrintAttributes) invocation.getArguments()[1]; 201 202 PrinterCapabilitiesTest.this.notify(); 203 } 204 205 callback.onLayoutFinished(info, true); 206 return null; 207 } 208 }, 209 new Answer<Void>() { 210 @Override 211 public Void answer(InvocationOnMock invocation) throws Throwable { 212 Object[] args = invocation.getArguments(); 213 PageRange[] pages = (PageRange[]) args[0]; 214 ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1]; 215 WriteResultCallback callback = (WriteResultCallback) args[3]; 216 217 writeBlankPages(layoutAttributes[0], fd, pages[0].getStart(), 218 pages[0].getEnd()); 219 fd.close(); 220 221 synchronized (PrinterCapabilitiesTest.this) { 222 writeAttributes[0] = layoutAttributes[0]; 223 224 PrinterCapabilitiesTest.this.notify(); 225 } 226 227 callback.onWriteFinished(pages); 228 return null; 229 } 230 }, null); 231 232 // Start printing. 233 print(adapter); 234 235 // Select the printer. 236 selectPrinter(PRINTER_NAME); 237 238 changeCapabilities(session[0], printerId[0], msBefore, availBefore); 239 if (availBefore && msBefore != null) { 240 waitForMediaSizeChange(layoutAttributes, msBefore); 241 waitForMediaSizeChange(writeAttributes, msBefore); 242 } else { 243 waitForPrinterUnavailable(); 244 } 245 246 changeCapabilities(session[0], printerId[0], msAfter, availAfter); 247 if (availAfter && msAfter != null) { 248 waitForMediaSizeChange(layoutAttributes, msAfter); 249 waitForMediaSizeChange(writeAttributes, msAfter); 250 } else { 251 waitForPrinterUnavailable(); 252 } 253 254 // Reset printer to default in case discovery session is reused 255 changeCapabilities(session[0], printerId[0], MediaSize.NA_LETTER, true); 256 waitForMediaSizeChange(layoutAttributes, MediaSize.NA_LETTER); 257 waitForMediaSizeChange(writeAttributes, MediaSize.NA_LETTER); 258 259 getUiDevice().pressBack(); 260 261 // Wait until PrintActivity is gone 262 Thread.sleep(500); 263 } 264 265 /** 266 * Tests that the printActivity propertly requests (layout and write) updates when the printer 267 * capabilities change. This tests all combination of changes. 268 * 269 * @throws Exception If something is unexpected. 270 */ testPrinterCapabilityChange()271 public void testPrinterCapabilityChange() throws Exception { 272 // The session might be reused between test cases, hence carry them from test case to case 273 final StubbablePrinterDiscoverySession[] session = new StubbablePrinterDiscoverySession[1]; 274 final PrinterId[] printerId = new PrinterId[1]; 275 276 // Create the session[0] callbacks that we will be checking. 277 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 278 createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() { 279 @Override 280 public Void answer(InvocationOnMock invocation) { 281 session[0] = ((PrinterDiscoverySessionCallbacks) invocation.getMock()) 282 .getSession(); 283 284 printerId[0] = session[0].getService().generatePrinterId(PRINTER_NAME); 285 286 session[0].addPrinters(generatePrinters(printerId[0], MediaSize.NA_LETTER, 287 PrinterInfo.STATUS_IDLE)); 288 return null; 289 } 290 }, null, null, null, null, null, new Answer<Void>() { 291 @Override 292 public Void answer(InvocationOnMock invocation) { 293 onPrinterDiscoverySessionDestroyCalled(); 294 return null; 295 } 296 }); 297 298 // Create the service callbacks for the first print service. 299 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 300 new Answer<PrinterDiscoverySessionCallbacks>() { 301 @Override 302 public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) { 303 return firstSessionCallbacks; 304 } 305 }, null, null); 306 307 // Configure the print services. 308 FirstPrintService.setCallbacks(firstServiceCallbacks); 309 SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null)); 310 311 testCase(session, printerId, false, null, false, null); 312 testCase(session, printerId, false, null, false, MediaSize.ISO_A0); 313 testCase(session, printerId, false, null, false, MediaSize.ISO_B0); 314 testCase(session, printerId, false, null, true, null); 315 testCase(session, printerId, false, null, true, MediaSize.ISO_A0); 316 testCase(session, printerId, false, null, true, MediaSize.ISO_B0); 317 testCase(session, printerId, false, MediaSize.ISO_A0, false, null); 318 testCase(session, printerId, false, MediaSize.ISO_A0, false, MediaSize.ISO_A0); 319 testCase(session, printerId, false, MediaSize.ISO_A0, false, MediaSize.ISO_B0); 320 testCase(session, printerId, false, MediaSize.ISO_A0, true, null); 321 testCase(session, printerId, false, MediaSize.ISO_A0, true, MediaSize.ISO_A0); 322 testCase(session, printerId, false, MediaSize.ISO_A0, true, MediaSize.ISO_B0); 323 testCase(session, printerId, true, null, false, null); 324 testCase(session, printerId, true, null, false, MediaSize.ISO_B0); 325 testCase(session, printerId, true, null, true, null); 326 testCase(session, printerId, true, null, true, MediaSize.ISO_B0); 327 testCase(session, printerId, true, MediaSize.ISO_A0, false, null); 328 testCase(session, printerId, true, MediaSize.ISO_A0, false, MediaSize.ISO_A0); 329 testCase(session, printerId, true, MediaSize.ISO_A0, false, MediaSize.ISO_B0); 330 testCase(session, printerId, true, MediaSize.ISO_A0, true, null); 331 testCase(session, printerId, true, MediaSize.ISO_A0, true, MediaSize.ISO_A0); 332 testCase(session, printerId, true, MediaSize.ISO_A0, true, MediaSize.ISO_B0); 333 334 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 335 } 336 337 /** 338 * Run a runnable and expect and exception of a certain type. 339 * 340 * @param r The runnable to run 341 * @param expectedClass The expected exception type 342 */ assertException(Runnable r, Class<? extends RuntimeException> expectedClass)343 private void assertException(Runnable r, Class<? extends RuntimeException> expectedClass) { 344 try { 345 r.run(); 346 } catch (Exception e) { 347 if (e.getClass().isAssignableFrom(expectedClass)) { 348 return; 349 } else { 350 throw new AssertionError("Expected: " + expectedClass.getName() + ", got: " 351 + e.getClass().getName()); 352 } 353 } 354 355 throw new AssertionError("No exception thrown"); 356 } 357 358 /** 359 * That that you cannot create illegal PrinterCapabilityInfos. 360 * 361 * @throws Exception If anything is unexpected 362 */ testIllegalPrinterCapabilityInfos()363 public void testIllegalPrinterCapabilityInfos() throws Exception { 364 if (!supportsPrinting()) { 365 return; 366 } 367 368 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 369 createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() { 370 @Override 371 public Void answer(InvocationOnMock invocation) { 372 StubbablePrinterDiscoverySession session = 373 ((PrinterDiscoverySessionCallbacks) 374 invocation.getMock()).getSession(); 375 376 PrinterId printerId = session.getService().generatePrinterId(PRINTER_NAME); 377 378 // printerId need to be set 379 assertException(() -> new PrinterCapabilitiesInfo.Builder(null), 380 IllegalArgumentException.class); 381 382 // All capability fields (beside duplex) need to be initialized: 383 // Test no color 384 assertException(() -> 385 (new PrinterCapabilitiesInfo.Builder(printerId)) 386 .setMinMargins(DEFAULT_MARGINS) 387 .addMediaSize(MediaSize.ISO_A4, true) 388 .addResolution(RESOLUTION_300, true).build(), 389 IllegalStateException.class); 390 // Test bad colors 391 assertException(() -> 392 (new PrinterCapabilitiesInfo.Builder(printerId)) 393 .setColorModes(0xffff, 394 PrintAttributes.COLOR_MODE_MONOCHROME), 395 IllegalArgumentException.class); 396 // Test bad duplex mode 397 assertException(() -> 398 (new PrinterCapabilitiesInfo.Builder(printerId)) 399 .setDuplexModes(0xffff, 400 PrintAttributes.DUPLEX_MODE_NONE), 401 IllegalArgumentException.class); 402 // Test no mediasize 403 assertException(() -> 404 (new PrinterCapabilitiesInfo.Builder(printerId)) 405 .setColorModes(PrintAttributes.COLOR_MODE_COLOR, 406 PrintAttributes.COLOR_MODE_COLOR) 407 .setMinMargins(DEFAULT_MARGINS) 408 .addResolution(RESOLUTION_300, true).build(), 409 IllegalStateException.class); 410 // Test no default mediasize 411 assertException(() -> 412 (new PrinterCapabilitiesInfo.Builder(printerId)) 413 .setColorModes(PrintAttributes.COLOR_MODE_COLOR, 414 PrintAttributes.COLOR_MODE_COLOR) 415 .setMinMargins(DEFAULT_MARGINS) 416 .addMediaSize(MediaSize.ISO_A4, false) 417 .addResolution(RESOLUTION_300, true).build(), 418 IllegalStateException.class); 419 // Test two default mediasizes 420 assertException(() -> 421 (new PrinterCapabilitiesInfo.Builder(printerId)) 422 .addMediaSize(MediaSize.ISO_A4, true) 423 .addMediaSize(MediaSize.ISO_A5, true), 424 IllegalArgumentException.class); 425 // Test no resolution 426 assertException(() -> 427 (new PrinterCapabilitiesInfo.Builder(printerId)) 428 .setColorModes(PrintAttributes.COLOR_MODE_COLOR, 429 PrintAttributes.COLOR_MODE_COLOR) 430 .setMinMargins(DEFAULT_MARGINS) 431 .addMediaSize(MediaSize.ISO_A4, true).build(), 432 IllegalStateException.class); 433 // Test no default resolution 434 assertException(() -> 435 (new PrinterCapabilitiesInfo.Builder(printerId)) 436 .setColorModes(PrintAttributes.COLOR_MODE_COLOR, 437 PrintAttributes.COLOR_MODE_COLOR) 438 .setMinMargins(DEFAULT_MARGINS) 439 .addMediaSize(MediaSize.ISO_A4, true) 440 .addResolution(RESOLUTION_300, false).build(), 441 IllegalStateException.class); 442 // Test two default resolutions 443 assertException(() -> 444 (new PrinterCapabilitiesInfo.Builder(printerId)) 445 .addResolution(RESOLUTION_300, true) 446 .addResolution(RESOLUTION_600, true), 447 IllegalArgumentException.class); 448 449 onPrinterDiscoverySessionCreateCalled(); 450 return null; 451 } 452 }, null, null, null, null, null, new Answer<Void>() { 453 @Override 454 public Void answer(InvocationOnMock invocation) { 455 onPrinterDiscoverySessionDestroyCalled(); 456 return null; 457 } 458 }); 459 460 // Create the service callbacks for the first print service. 461 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 462 new Answer<PrinterDiscoverySessionCallbacks>() { 463 @Override 464 public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) { 465 return firstSessionCallbacks; 466 } 467 }, null, null); 468 469 // Configure the print services. 470 FirstPrintService.setCallbacks(firstServiceCallbacks); 471 SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null)); 472 473 PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(null, null, null); 474 475 // Start printing. 476 print(adapter); 477 478 waitForPrinterDiscoverySessionCreateCallbackCalled(); 479 480 getActivity().finish(); 481 482 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 483 } 484 485 /** 486 * That that you can use all sane legal PrinterCapabilityInfos. 487 * 488 * @throws Exception If anything is unexpected 489 */ testSanePrinterCapabilityInfos()490 public void testSanePrinterCapabilityInfos() throws Exception { 491 if (!supportsPrinting()) { 492 return; 493 } 494 495 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 496 createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() { 497 @Override 498 public Void answer(InvocationOnMock invocation) { 499 StubbablePrinterDiscoverySession session = 500 ((PrinterDiscoverySessionCallbacks) 501 invocation.getMock()).getSession(); 502 503 MediaSize[] mediaSizes = {MediaSize.ISO_A0, MediaSize.ISO_A0, 504 MediaSize.ISO_A1}; 505 Resolution[] resolutions = {RESOLUTION_300, RESOLUTION_300, 506 RESOLUTION_600}; 507 int[] colorModes = {PrintAttributes.COLOR_MODE_MONOCHROME, 508 PrintAttributes.COLOR_MODE_COLOR}; 509 int[] duplexModes = {PrintAttributes.DUPLEX_MODE_NONE, 510 PrintAttributes.DUPLEX_MODE_LONG_EDGE, 511 PrintAttributes.DUPLEX_MODE_SHORT_EDGE}; 512 513 ArrayList<PrinterInfo> printers = new ArrayList<>(); 514 for (int mediaSizeIndex = 1; mediaSizeIndex < mediaSizes.length; 515 mediaSizeIndex++) { 516 for (int resolutionIndex = 1; resolutionIndex < mediaSizes.length; 517 resolutionIndex++) { 518 for (int colorIndex = 1; colorIndex < colorModes.length; 519 colorIndex++) { 520 for (int duplexIndex = 1; duplexIndex < duplexModes.length; 521 duplexIndex++) { 522 PrinterId printerId = session.getService() 523 .generatePrinterId(Integer.valueOf(printers.size()) 524 .toString()); 525 526 PrinterCapabilitiesInfo.Builder b = 527 new PrinterCapabilitiesInfo.Builder(printerId); 528 529 for (int i = 0; i < mediaSizeIndex; i++) { 530 b.addMediaSize(mediaSizes[i], i == mediaSizeIndex - 1); 531 } 532 533 for (int i = 0; i < resolutionIndex; i++) { 534 b.addResolution(resolutions[i], 535 i == resolutionIndex - 1); 536 } 537 538 int allColors = 0; 539 for (int i = 0; i < colorIndex; i++) { 540 allColors |= colorModes[i]; 541 } 542 b.setColorModes(allColors, colorModes[colorIndex - 1]); 543 544 int allDuplexModes = 0; 545 for (int i = 0; i < duplexIndex; i++) { 546 allDuplexModes |= duplexModes[i]; 547 } 548 b.setDuplexModes(allDuplexModes, 549 duplexModes[duplexIndex - 1]); 550 551 printers.add((new PrinterInfo.Builder(printerId, 552 Integer.valueOf(printers.size()).toString(), 553 PrinterInfo.STATUS_IDLE)).setCapabilities(b.build()) 554 .build()); 555 } 556 } 557 } 558 } 559 560 session.addPrinters(printers); 561 562 onPrinterDiscoverySessionCreateCalled(); 563 return null; 564 } 565 }, null, null, null, null, null, new Answer<Void>() { 566 @Override 567 public Void answer(InvocationOnMock invocation) { 568 onPrinterDiscoverySessionDestroyCalled(); 569 return null; 570 } 571 }); 572 573 // Create the service callbacks for the first print service. 574 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 575 new Answer<PrinterDiscoverySessionCallbacks>() { 576 @Override 577 public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) { 578 return firstSessionCallbacks; 579 } 580 }, null, null); 581 582 // Configure the print services. 583 FirstPrintService.setCallbacks(firstServiceCallbacks); 584 SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null)); 585 586 PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(null, null, null); 587 588 // Start printing. 589 print(adapter); 590 591 waitForPrinterDiscoverySessionCreateCallbackCalled(); 592 593 getUiDevice().pressBack(); 594 595 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 596 } 597 598 /** 599 * Base test that performs a print operation with a give PrinterCapabilityInfo and run a test 600 * function before finishing. 601 * 602 * @throws Exception 603 */ testPrinterCapabilityInfo(final Function<PrinterId, PrinterCapabilitiesInfo> capBuilder, Consumer<PrintAttributes> test)604 private void testPrinterCapabilityInfo(final Function<PrinterId, PrinterCapabilitiesInfo> 605 capBuilder, Consumer<PrintAttributes> test) throws Exception { 606 if (!supportsPrinting()) { 607 return; 608 } 609 610 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 611 createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() { 612 @Override 613 public Void answer(InvocationOnMock invocation) { 614 StubbablePrinterDiscoverySession session = 615 ((PrinterDiscoverySessionCallbacks) 616 invocation.getMock()).getSession(); 617 618 PrinterId printerId = session.getService() 619 .generatePrinterId(PRINTER_NAME); 620 621 ArrayList<PrinterInfo> printers = new ArrayList<>(); 622 printers.add((new PrinterInfo.Builder(printerId, PRINTER_NAME, 623 PrinterInfo.STATUS_IDLE)) 624 .setCapabilities(capBuilder.apply(printerId)).build()); 625 626 session.addPrinters(printers); 627 628 onPrinterDiscoverySessionCreateCalled(); 629 return null; 630 } 631 }, null, null, null, null, null, new Answer<Void>() { 632 @Override 633 public Void answer(InvocationOnMock invocation) { 634 onPrinterDiscoverySessionDestroyCalled(); 635 return null; 636 } 637 }); 638 639 // Create the service callbacks for the first print service. 640 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 641 new Answer<PrinterDiscoverySessionCallbacks>() { 642 @Override 643 public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) { 644 return firstSessionCallbacks; 645 } 646 }, null, null); 647 648 // Configure the print services. 649 FirstPrintService.setCallbacks(firstServiceCallbacks); 650 SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null)); 651 652 final PrintAttributes[] layoutAttributes = new PrintAttributes[1]; 653 654 PrintDocumentAdapter adapter = createMockPrintDocumentAdapter( 655 new Answer<Void>() { 656 @Override 657 public Void answer(InvocationOnMock invocation) throws Throwable { 658 LayoutResultCallback callback = (LayoutResultCallback) invocation 659 .getArguments()[3]; 660 PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME) 661 .setPageCount(1) 662 .build(); 663 layoutAttributes[0] = (PrintAttributes) invocation.getArguments()[1]; 664 665 callback.onLayoutFinished(info, true); 666 return null; 667 } 668 }, 669 new Answer<Void>() { 670 @Override 671 public Void answer(InvocationOnMock invocation) throws Throwable { 672 Object[] args = invocation.getArguments(); 673 PageRange[] pages = (PageRange[]) args[0]; 674 ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1]; 675 WriteResultCallback callback = (WriteResultCallback) args[3]; 676 677 writeBlankPages(layoutAttributes[0], fd, pages[0].getStart(), 678 pages[0].getEnd()); 679 fd.close(); 680 681 callback.onWriteFinished(pages); 682 return null; 683 } 684 }, null); 685 686 // Start printing. 687 print(adapter); 688 689 // make sure that options does not crash 690 openPrintOptions(); 691 692 // Select printer under test 693 selectPrinter(PRINTER_NAME); 694 695 clickPrintButton(); 696 697 answerPrintServicesWarning(true); 698 699 test.accept(layoutAttributes[0]); 700 701 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 702 } 703 704 /** 705 * That that you use a default color that is not in the allowed colors. This is allowed because 706 * of historical reasons. 707 * 708 * @throws Exception If anything is unexpected 709 */ testInvalidDefaultColor()710 public void testInvalidDefaultColor() throws Exception { 711 testPrinterCapabilityInfo( 712 (printerId) -> (new PrinterCapabilitiesInfo.Builder(printerId)) 713 .addMediaSize(MediaSize.ISO_A4, true) 714 .addResolution(RESOLUTION_300, true) 715 .setColorModes(PrintAttributes.COLOR_MODE_MONOCHROME, 716 PrintAttributes.COLOR_MODE_COLOR).build(), 717 (layoutAttributes) -> assertEquals(layoutAttributes.getColorMode(), 718 PrintAttributes.COLOR_MODE_MONOCHROME)); 719 } 720 721 /** 722 * That that you use a default duplex mode that is not in the allowed duplex modes. This is 723 * allowed because of historical reasons. 724 * 725 * @throws Exception If anything is unexpected 726 */ testInvalidDefaultDuplexMode()727 public void testInvalidDefaultDuplexMode() throws Exception { 728 testPrinterCapabilityInfo( 729 (printerId) -> (new PrinterCapabilitiesInfo.Builder(printerId)) 730 .addMediaSize(MediaSize.ISO_A4, true) 731 .addResolution(RESOLUTION_300, true) 732 .setColorModes(PrintAttributes.COLOR_MODE_MONOCHROME, 733 PrintAttributes.COLOR_MODE_MONOCHROME) 734 .setDuplexModes(PrintAttributes.DUPLEX_MODE_LONG_EDGE 735 | PrintAttributes.DUPLEX_MODE_NONE, 736 PrintAttributes.DUPLEX_MODE_SHORT_EDGE).build(), 737 (layoutAttributes) -> assertTrue(layoutAttributes.getDuplexMode() == 738 PrintAttributes.DUPLEX_MODE_LONG_EDGE || layoutAttributes.getDuplexMode() == 739 PrintAttributes.DUPLEX_MODE_NONE)); 740 } 741 } 742