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 android.print.test.Utils.eventually; 20 import static android.print.test.Utils.runOnMainThread; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertFalse; 24 import static org.junit.Assert.assertNotNull; 25 import static org.junit.Assert.assertTrue; 26 import static org.mockito.Mockito.inOrder; 27 28 import android.print.PrintAttributes; 29 import android.print.PrintAttributes.Margins; 30 import android.print.PrintAttributes.MediaSize; 31 import android.print.PrintAttributes.Resolution; 32 import android.print.PrintDocumentAdapter; 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.FirstPrintService; 38 import android.print.test.services.PrintServiceCallbacks; 39 import android.print.test.services.PrinterDiscoverySessionCallbacks; 40 import android.print.test.services.SecondPrintService; 41 import android.print.test.services.StubbablePrinterDiscoverySession; 42 import android.printservice.PrintJob; 43 import android.printservice.PrinterDiscoverySession; 44 45 import androidx.annotation.NonNull; 46 import androidx.test.runner.AndroidJUnit4; 47 48 import org.junit.Before; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.InOrder; 52 import org.mockito.exceptions.verification.VerificationInOrderFailure; 53 54 import java.util.ArrayList; 55 import java.util.Collections; 56 import java.util.List; 57 58 /** 59 * This test verifies that the system respects the {@link PrinterDiscoverySession} 60 * contract is respected. 61 */ 62 @RunWith(AndroidJUnit4.class) 63 public class PrinterDiscoverySessionLifecycleTest extends BasePrintTest { 64 private static final String FIRST_PRINTER_LOCAL_ID = "first_printer"; 65 private static final String SECOND_PRINTER_LOCAL_ID = "second_printer"; 66 67 private static StubbablePrinterDiscoverySession sSession; 68 69 @Before clearPrintSpoolerState()70 public void clearPrintSpoolerState() throws Exception { 71 clearPrintSpoolerData(); 72 } 73 74 /** 75 * Add a printer to {@#sSession}. 76 * 77 * @param localId The id of the printer to add 78 * @param hasCapabilities If the printer has capabilities 79 */ addPrinter(@onNull String localId, boolean hasCapabilities)80 private void addPrinter(@NonNull String localId, boolean hasCapabilities) { 81 // Add the first printer. 82 PrinterId firstPrinterId = sSession.getService().generatePrinterId( 83 localId); 84 85 PrinterInfo.Builder printer = new PrinterInfo.Builder(firstPrinterId, 86 localId, PrinterInfo.STATUS_IDLE); 87 88 if (hasCapabilities) { 89 printer.setCapabilities(new PrinterCapabilitiesInfo.Builder(firstPrinterId) 90 .setMinMargins(new Margins(200, 200, 200, 200)) 91 .addMediaSize(MediaSize.ISO_A0, true) 92 .addResolution(new Resolution("300x300", "300x300", 300, 300), true) 93 .setColorModes(PrintAttributes.COLOR_MODE_COLOR, 94 PrintAttributes.COLOR_MODE_COLOR) 95 .build()); 96 } 97 98 sSession.addPrinters(Collections.singletonList(printer.build())); 99 } 100 101 /** 102 * Make {@code localPrinterId} the default printer. This requires a full print workflow. 103 * 104 * As a side-effect also approved the print service. 105 * 106 * @param localPrinterId The printer to make default 107 */ makeDefaultPrinter(String localPrinterId)108 private void makeDefaultPrinter(String localPrinterId) throws Throwable { 109 PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1); 110 111 print(adapter); 112 waitForWriteAdapterCallback(1); 113 114 runOnMainThread(() -> addPrinter(localPrinterId, true)); 115 selectPrinter(localPrinterId); 116 waitForWriteAdapterCallback(2); 117 118 mPrintHelper.submitPrintJob(); 119 120 eventually(() -> { 121 answerPrintServicesWarning(true); 122 123 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 124 }, OPERATION_TIMEOUT_MILLIS * 2); 125 126 resetCounters(); 127 } 128 129 /** 130 * Select a printer in the all printers activity 131 * 132 * @param printerName The name of the printer to select 133 */ selectInAllPrintersActivity(@onNull String printerName)134 private void selectInAllPrintersActivity(@NonNull String printerName) throws Exception { 135 mPrintHelper.selectPrinterWhenAvailable(printerName); 136 } 137 138 @Test defaultPrinterBecomesAvailableWhileInBackground()139 public void defaultPrinterBecomesAvailableWhileInBackground() throws Throwable { 140 // Create the session callbacks that we will be checking. 141 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 142 createMockPrinterDiscoverySessionCallbacks(invocation -> { 143 sSession = 144 ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession(); 145 146 onPrinterDiscoverySessionCreateCalled(); 147 return null; 148 }, null, null, null, null, null, invocation -> { 149 onPrinterDiscoverySessionDestroyCalled(); 150 return null; 151 }); 152 153 // Create the service callbacks for the first print service. 154 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 155 invocation -> firstSessionCallbacks, null, null); 156 157 // Configure the print services. 158 FirstPrintService.setCallbacks(firstServiceCallbacks); 159 SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks()); 160 161 makeDefaultPrinter(FIRST_PRINTER_LOCAL_ID); 162 163 PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1); 164 print(adapter); 165 waitForPrinterDiscoverySessionCreateCallbackCalled(); 166 167 waitForPrinterUnavailable(); 168 169 selectPrinter("All printers…"); 170 // Let all printers activity start 171 Thread.sleep(500); 172 173 // Add printer 174 runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, true)); 175 176 // Select printer once available (this returns to main print activity) 177 selectInAllPrintersActivity(FIRST_PRINTER_LOCAL_ID); 178 179 // Wait for preview to load and finish print 180 waitForWriteAdapterCallback(1); 181 182 eventually( 183 () -> { 184 mPrintHelper.submitPrintJob(); 185 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 186 }, 187 OPERATION_TIMEOUT_MILLIS * 2); 188 } 189 190 @Test defaultPrinterBecomesUsableWhileInBackground()191 public void defaultPrinterBecomesUsableWhileInBackground() throws Throwable { 192 // Create the session callbacks that we will be checking. 193 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 194 createMockPrinterDiscoverySessionCallbacks(invocation -> { 195 sSession = 196 ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession(); 197 198 onPrinterDiscoverySessionCreateCalled(); 199 return null; 200 }, null, null, null, null, null, invocation -> { 201 onPrinterDiscoverySessionDestroyCalled(); 202 return null; 203 }); 204 205 // Create the service callbacks for the first print service. 206 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 207 invocation -> firstSessionCallbacks, null, null); 208 209 // Configure the print services. 210 FirstPrintService.setCallbacks(firstServiceCallbacks); 211 SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks()); 212 213 makeDefaultPrinter(FIRST_PRINTER_LOCAL_ID); 214 215 PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1); 216 print(adapter); 217 waitForPrinterDiscoverySessionCreateCallbackCalled(); 218 219 // Add printer but do not enable it (capabilities == null) 220 runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, false)); 221 waitForPrinterUnavailable(); 222 223 selectPrinter("All printers…"); 224 // Let all printers activity start 225 Thread.sleep(500); 226 227 // Enable printer 228 runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, true)); 229 230 // Select printer once available (this returns to main print activity) 231 selectInAllPrintersActivity(FIRST_PRINTER_LOCAL_ID); 232 233 // Wait for preview to load and finish print 234 waitForWriteAdapterCallback(1); 235 236 eventually( 237 () -> { 238 mPrintHelper.submitPrintJob(); 239 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 240 }, 241 OPERATION_TIMEOUT_MILLIS * 2); 242 } 243 244 @Test normalLifecycle()245 public void normalLifecycle() throws Throwable { 246 // Create the session callbacks that we will be checking. 247 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 248 createFirstMockPrinterDiscoverySessionCallbacks(false); 249 250 // Create the service callbacks for the first print service. 251 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 252 invocation -> firstSessionCallbacks, 253 invocation -> { 254 PrintJob printJob = (PrintJob) invocation.getArguments()[0]; 255 // We pretend the job is handled immediately. 256 printJob.complete(); 257 return null; 258 }, null); 259 260 // Configure the print services. 261 FirstPrintService.setCallbacks(firstServiceCallbacks); 262 SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks()); 263 264 // Create a print adapter that respects the print contract. 265 PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1); 266 267 // Start printing. 268 print(adapter); 269 270 // Wait for write of the first page. 271 waitForWriteAdapterCallback(1); 272 273 runOnMainThread(() -> { 274 addPrinter(FIRST_PRINTER_LOCAL_ID, false); 275 addPrinter(SECOND_PRINTER_LOCAL_ID, false); 276 }); 277 278 runOnMainThread(() -> assertFalse(sSession.isDestroyed())); 279 runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size())); 280 281 // Select the first printer. 282 selectPrinter(FIRST_PRINTER_LOCAL_ID); 283 284 eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID, 285 sSession.getTrackedPrinters().get(0).getLocalId()))); 286 runOnMainThread(() -> assertTrue(sSession.isPrinterDiscoveryStarted())); 287 runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size())); 288 289 // Wait for layout as the printer has different capabilities. 290 waitForLayoutAdapterCallbackCount(2); 291 292 // Select the second printer (same capabilities as the other 293 // one so no layout should happen). 294 selectPrinter(SECOND_PRINTER_LOCAL_ID); 295 296 eventually(() -> runOnMainThread(() -> assertEquals(SECOND_PRINTER_LOCAL_ID, 297 sSession.getTrackedPrinters().get(0).getLocalId()))); 298 runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size())); 299 300 // While the printer discovery session is still alive store the 301 // ids of printers as we want to make some assertions about them 302 // but only the print service can create printer ids which means 303 // that we need to get the created ones. 304 PrinterId firstPrinterId = getAddedPrinterIdForLocalId( 305 FIRST_PRINTER_LOCAL_ID); 306 PrinterId secondPrinterId = getAddedPrinterIdForLocalId( 307 SECOND_PRINTER_LOCAL_ID); 308 assertNotNull("Coundn't find printer:" + FIRST_PRINTER_LOCAL_ID, firstPrinterId); 309 assertNotNull("Coundn't find printer:" + SECOND_PRINTER_LOCAL_ID, secondPrinterId); 310 311 // Click the print button. 312 mPrintHelper.submitPrintJob(); 313 314 eventually(() -> { 315 // Answer the dialog for the print service cloud warning 316 answerPrintServicesWarning(true); 317 318 // Wait for all print jobs to be handled after which the session destroyed. 319 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 320 }, OPERATION_TIMEOUT_MILLIS * 2); 321 322 runOnMainThread(() -> assertTrue(sSession.isDestroyed())); 323 runOnMainThread(() -> assertFalse(sSession.isPrinterDiscoveryStarted())); 324 runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size())); 325 326 // Verify the expected calls. 327 InOrder inOrder = inOrder(firstSessionCallbacks); 328 329 // We start discovery as the print dialog was up. 330 List<PrinterId> emptyPrinterIdList = Collections.emptyList(); 331 inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery( 332 emptyPrinterIdList); 333 334 // We selected the first printer and now it should be tracked. 335 inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking( 336 firstPrinterId); 337 338 // We selected the second printer so the first should not be tracked. 339 inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking( 340 firstPrinterId); 341 342 // We selected the second printer and now it should be tracked. 343 inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking( 344 secondPrinterId); 345 346 // The print dialog went away so we first stop the printer tracking... 347 inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking( 348 secondPrinterId); 349 350 // ... next we stop printer discovery... 351 inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery(); 352 353 // ... last the session is destroyed. 354 inOrder.verify(firstSessionCallbacks).onDestroy(); 355 } 356 357 @Test cancelPrintServicesAlertDialog()358 public void cancelPrintServicesAlertDialog() throws Throwable { 359 // Create the session callbacks that we will be checking. 360 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 361 createFirstMockPrinterDiscoverySessionCallbacks(false); 362 363 // Create the service callbacks for the first print service. 364 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 365 invocation -> firstSessionCallbacks, 366 invocation -> { 367 PrintJob printJob = (PrintJob) invocation.getArguments()[0]; 368 // We pretend the job is handled immediately. 369 printJob.complete(); 370 return null; 371 }, null); 372 373 // Configure the print services. 374 FirstPrintService.setCallbacks(firstServiceCallbacks); 375 SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks()); 376 377 // Create a print adapter that respects the print contract. 378 PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1); 379 380 // Start printing. 381 print(adapter); 382 383 // Wait for write of the first page. 384 waitForWriteAdapterCallback(1); 385 386 runOnMainThread(() -> { 387 addPrinter(FIRST_PRINTER_LOCAL_ID, false); 388 addPrinter(SECOND_PRINTER_LOCAL_ID, false); 389 }); 390 391 runOnMainThread(() -> assertFalse(sSession.isDestroyed())); 392 runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size())); 393 394 // Select the first printer. 395 selectPrinter(FIRST_PRINTER_LOCAL_ID); 396 397 eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID, 398 sSession.getTrackedPrinters().get(0).getLocalId()))); 399 runOnMainThread(() -> assertTrue(sSession.isPrinterDiscoveryStarted())); 400 runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size())); 401 402 // While the printer discovery session is still alive store the 403 // ids of printers as we want to make some assertions about them 404 // but only the print service can create printer ids which means 405 // that we need to get the created ones. 406 PrinterId firstPrinterId = getAddedPrinterIdForLocalId( 407 FIRST_PRINTER_LOCAL_ID); 408 assertNotNull("Coundn't find printer:" + FIRST_PRINTER_LOCAL_ID, firstPrinterId); 409 410 // Click the print button. 411 mPrintHelper.submitPrintJob(); 412 413 // Cancel the dialog for the print service cloud warning 414 answerPrintServicesWarning(false); 415 416 // Click the print button again. 417 mPrintHelper.submitPrintJob(); 418 419 // Answer the dialog for the print service cloud warning 420 answerPrintServicesWarning(true); 421 422 // Wait for all print jobs to be handled after which the session destroyed. 423 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 424 425 runOnMainThread(() -> assertTrue(sSession.isDestroyed())); 426 runOnMainThread(() -> assertFalse(sSession.isPrinterDiscoveryStarted())); 427 runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size())); 428 429 // Verify the expected calls. 430 InOrder inOrder = inOrder(firstSessionCallbacks); 431 432 // We start discovery as the print dialog was up. 433 List<PrinterId> emptyPrinterIdList = Collections.emptyList(); 434 inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery( 435 emptyPrinterIdList); 436 437 // We selected the first printer and now it should be tracked. 438 inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking( 439 firstPrinterId); 440 441 // We selected the second printer so the first should not be tracked. 442 inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking( 443 firstPrinterId); 444 445 // ... next we stop printer discovery... 446 inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery(); 447 448 // ... last the session is destroyed. 449 inOrder.verify(firstSessionCallbacks).onDestroy(); 450 } 451 452 @Test startPrinterDiscoveryWithHistoricalPrinters()453 public void startPrinterDiscoveryWithHistoricalPrinters() throws Throwable { 454 // Create the session callbacks that we will be checking. 455 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 456 createFirstMockPrinterDiscoverySessionCallbacks(false); 457 458 // Create the service callbacks for the first print service. 459 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 460 invocation -> firstSessionCallbacks, 461 invocation -> { 462 PrintJob printJob = (PrintJob) invocation.getArguments()[0]; 463 // We pretend the job is handled immediately. 464 printJob.complete(); 465 return null; 466 }, null); 467 468 // Configure the print services. 469 FirstPrintService.setCallbacks(firstServiceCallbacks); 470 SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks()); 471 472 // Create a print adapter that respects the print contract. 473 PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1); 474 475 // Start printing. 476 print(adapter); 477 478 // Wait for write of the first page. 479 waitForWriteAdapterCallback(1); 480 481 runOnMainThread(() -> { 482 addPrinter(FIRST_PRINTER_LOCAL_ID, false); 483 addPrinter(SECOND_PRINTER_LOCAL_ID, false); 484 }); 485 486 runOnMainThread(() -> assertFalse(sSession.isDestroyed())); 487 runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size())); 488 489 // Select the first printer. 490 selectPrinter(FIRST_PRINTER_LOCAL_ID); 491 492 eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID, 493 sSession.getTrackedPrinters().get(0).getLocalId()))); 494 runOnMainThread(() -> assertTrue(sSession.isPrinterDiscoveryStarted())); 495 runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size())); 496 497 // Wait for a layout to finish - first layout was for the 498 // PDF printer, second for the first printer in preview mode. 499 waitForLayoutAdapterCallbackCount(2); 500 501 // While the printer discovery session is still alive store the 502 // ids of printer as we want to make some assertions about it 503 // but only the print service can create printer ids which means 504 // that we need to get the created one. 505 PrinterId firstPrinterId = getAddedPrinterIdForLocalId( 506 FIRST_PRINTER_LOCAL_ID); 507 508 // Click the print button. 509 mPrintHelper.submitPrintJob(); 510 511 eventually(() -> { 512 // Answer the dialog for the print service cloud warning 513 answerPrintServicesWarning(true); 514 515 // Wait for the print to complete. 516 waitForAdapterFinishCallbackCalled(); 517 }, OPERATION_TIMEOUT_MILLIS * 2); 518 519 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 520 521 // Set up print service to immediately report the printers 522 final PrinterDiscoverySessionCallbacks firstSessionCallbacksWithPrinters = 523 createFirstMockPrinterDiscoverySessionCallbacks(true); 524 PrintServiceCallbacks firstServiceCallbacksWithPrinters = createMockPrintServiceCallbacks( 525 invocation -> firstSessionCallbacksWithPrinters, 526 invocation -> null, null); 527 FirstPrintService.setCallbacks(firstServiceCallbacksWithPrinters); 528 529 // Now print again as we want to confirm that the start 530 // printer discovery passes in the priority list. 531 print(adapter); 532 533 // Wait for a layout to finish - first layout was for the 534 // PDF printer, second for the first printer in preview mode, 535 // the third for the first printer in non-preview mode, and 536 // now a fourth for the PDF printer as we are printing again. 537 waitForLayoutAdapterCallbackCount(4); 538 539 // Cancel the printing. 540 mPrintHelper.cancelPrinting(); 541 542 // Wait for all print jobs to be handled after which the is session destroyed. 543 waitForPrinterDiscoverySessionDestroyCallbackCalled(2); 544 545 runOnMainThread(() -> assertTrue(sSession.isDestroyed())); 546 runOnMainThread(() -> assertFalse(sSession.isPrinterDiscoveryStarted())); 547 runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size())); 548 549 // Verify the expected calls. 550 InOrder inOrder = inOrder(firstSessionCallbacks, firstSessionCallbacksWithPrinters); 551 552 // We start discovery with no printer history. 553 List<PrinterId> priorityList = new ArrayList<>(); 554 inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery( 555 priorityList); 556 557 // We selected the first printer and now it should be tracked. 558 inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking( 559 firstPrinterId); 560 561 // We confirmed print so the first should not be tracked. 562 inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking( 563 firstPrinterId); 564 565 inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery(); 566 inOrder.verify(firstSessionCallbacks).onDestroy(); 567 568 priorityList.add(firstPrinterId); 569 inOrder.verify(firstSessionCallbacksWithPrinters).onStartPrinterDiscovery(priorityList); 570 571 // The system selects the highest ranked historical printer. 572 inOrder.verify(firstSessionCallbacksWithPrinters).onStartPrinterStateTracking( 573 firstPrinterId); 574 575 // We canceled print so the first should not be tracked. 576 inOrder.verify(firstSessionCallbacksWithPrinters).onStopPrinterStateTracking( 577 firstPrinterId); 578 579 580 // Discovery is always stopped before the session is always destroyed. 581 inOrder.verify(firstSessionCallbacksWithPrinters).onStopPrinterDiscovery(); 582 583 // ...last the session is destroyed. 584 inOrder.verify(firstSessionCallbacksWithPrinters).onDestroy(); 585 } 586 587 @Test addRemovePrinters()588 public void addRemovePrinters() throws Throwable { 589 StubbablePrinterDiscoverySession[] session = new StubbablePrinterDiscoverySession[1]; 590 591 // Create the session callbacks that we will be checking. 592 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 593 createMockPrinterDiscoverySessionCallbacks(invocation -> { 594 session[0] = ((PrinterDiscoverySessionCallbacks) 595 invocation.getMock()).getSession(); 596 597 onPrinterDiscoverySessionCreateCalled(); 598 return null; 599 }, null, null, null, null, null, invocation -> { 600 onPrinterDiscoverySessionDestroyCalled(); 601 return null; 602 }); 603 604 // Create the service callbacks for the first print service. 605 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 606 invocation -> firstSessionCallbacks, null, null); 607 608 // Configure the print services. 609 FirstPrintService.setCallbacks(firstServiceCallbacks); 610 SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks()); 611 612 print(createDefaultPrintDocumentAdapter(1)); 613 614 waitForPrinterDiscoverySessionCreateCallbackCalled(); 615 616 runOnMainThread(() -> assertEquals(0, session[0].getPrinters().size())); 617 618 PrinterId[] printerIds = new PrinterId[3]; 619 runOnMainThread(() -> { 620 printerIds[0] = session[0].getService().generatePrinterId("0"); 621 printerIds[1] = session[0].getService().generatePrinterId("1"); 622 printerIds[2] = session[0].getService().generatePrinterId("2"); 623 }); 624 625 PrinterInfo printer1 = (new PrinterInfo.Builder(printerIds[0], "0", 626 PrinterInfo.STATUS_IDLE)).build(); 627 628 PrinterInfo printer2 = (new PrinterInfo.Builder(printerIds[1], "1", 629 PrinterInfo.STATUS_IDLE)).build(); 630 631 PrinterInfo printer3 = (new PrinterInfo.Builder(printerIds[2], "2", 632 PrinterInfo.STATUS_IDLE)).build(); 633 634 ArrayList<PrinterInfo> printers = new ArrayList<>(); 635 printers.add(printer1); 636 runOnMainThread(() -> session[0].addPrinters(printers)); 637 eventually(() -> runOnMainThread(() -> assertEquals(1, session[0].getPrinters().size()))); 638 639 printers.add(printer2); 640 printers.add(printer3); 641 runOnMainThread(() -> session[0].addPrinters(printers)); 642 eventually(() -> runOnMainThread(() -> assertEquals(3, session[0].getPrinters().size()))); 643 644 ArrayList<PrinterId> printerIdsToRemove = new ArrayList<>(); 645 printerIdsToRemove.add(printer1.getId()); 646 runOnMainThread(() -> session[0].removePrinters(printerIdsToRemove)); 647 eventually(() -> runOnMainThread(() -> assertEquals(2, session[0].getPrinters().size()))); 648 649 printerIdsToRemove.add(printer2.getId()); 650 printerIdsToRemove.add(printer3.getId()); 651 runOnMainThread(() -> session[0].removePrinters(printerIdsToRemove)); 652 eventually(() -> runOnMainThread(() -> assertEquals(0, session[0].getPrinters().size()))); 653 654 mPrintHelper.cancelPrinting(); 655 656 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 657 } 658 getAddedPrinterIdForLocalId(String printerLocalId)659 private PrinterId getAddedPrinterIdForLocalId(String printerLocalId) throws Throwable { 660 final List<PrinterInfo> reportedPrinters = new ArrayList<>(); 661 runOnMainThread(() -> { 662 // Grab the printer ids as only the service can create such. 663 reportedPrinters.addAll(sSession.getPrinters()); 664 }); 665 666 final int reportedPrinterCount = reportedPrinters.size(); 667 for (int i = 0; i < reportedPrinterCount; i++) { 668 PrinterInfo reportedPrinter = reportedPrinters.get(i); 669 String localId = reportedPrinter.getId().getLocalId(); 670 if (printerLocalId.equals(localId)) { 671 return reportedPrinter.getId(); 672 } 673 } 674 675 return null; 676 } 677 createSecondMockPrintServiceCallbacks()678 private PrintServiceCallbacks createSecondMockPrintServiceCallbacks() { 679 return createMockPrintServiceCallbacks(null, null, null); 680 } 681 createFirstMockPrinterDiscoverySessionCallbacks( boolean shouldAddPrinters)682 private PrinterDiscoverySessionCallbacks createFirstMockPrinterDiscoverySessionCallbacks( 683 boolean shouldAddPrinters) { 684 return createMockPrinterDiscoverySessionCallbacks(invocation -> { 685 // Get the session. 686 sSession = ((PrinterDiscoverySessionCallbacks) 687 invocation.getMock()).getSession(); 688 689 assertTrue(sSession.isPrinterDiscoveryStarted()); 690 691 if (shouldAddPrinters) { 692 addPrinter(FIRST_PRINTER_LOCAL_ID, false); 693 addPrinter(SECOND_PRINTER_LOCAL_ID, false); 694 } 695 696 return null; 697 }, invocation -> { 698 assertFalse(sSession.isPrinterDiscoveryStarted()); 699 return null; 700 }, null, invocation -> { 701 // Get the session. 702 StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks) 703 invocation.getMock()).getSession(); 704 705 PrinterId trackedPrinterId = (PrinterId) invocation.getArguments()[0]; 706 List<PrinterInfo> reportedPrinters = session.getPrinters(); 707 708 // We should be tracking a printer that we added. 709 PrinterInfo trackedPrinter = null; 710 final int reportedPrinterCount = reportedPrinters.size(); 711 for (int i = 0; i < reportedPrinterCount; i++) { 712 PrinterInfo reportedPrinter = reportedPrinters.get(i); 713 if (reportedPrinter.getId().equals(trackedPrinterId)) { 714 trackedPrinter = reportedPrinter; 715 break; 716 } 717 } 718 assertNotNull("Can track only added printers", trackedPrinter); 719 720 assertTrue(sSession.getTrackedPrinters().contains(trackedPrinter.getId())); 721 assertEquals(1, sSession.getTrackedPrinters().size()); 722 723 // If the printer does not have capabilities reported add them. 724 if (trackedPrinter.getCapabilities() == null) { 725 726 // Add the capabilities to emulate lazy discovery. 727 // Same for each printer is fine for what we test. 728 PrinterCapabilitiesInfo capabilities = 729 new PrinterCapabilitiesInfo.Builder(trackedPrinterId) 730 .setMinMargins(new Margins(200, 200, 200, 200)) 731 .addMediaSize(MediaSize.ISO_A4, true) 732 .addMediaSize(MediaSize.ISO_A5, false) 733 .addResolution(new Resolution("300x300", "300x300", 300, 300), true) 734 .setColorModes(PrintAttributes.COLOR_MODE_COLOR, 735 PrintAttributes.COLOR_MODE_COLOR) 736 .build(); 737 PrinterInfo updatedPrinter = new PrinterInfo.Builder(trackedPrinter) 738 .setCapabilities(capabilities) 739 .build(); 740 741 // Update the printer. 742 List<PrinterInfo> printers = new ArrayList<>(); 743 printers.add(updatedPrinter); 744 session.addPrinters(printers); 745 } 746 747 return null; 748 }, null, null, invocation -> { 749 assertTrue(sSession.isDestroyed()); 750 751 // Take a note onDestroy was called. 752 onPrinterDiscoverySessionDestroyCalled(); 753 return null; 754 }); 755 } 756 } 757