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.assertException;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.os.ParcelFileDescriptor;
25 import android.print.PageRange;
26 import android.print.PrintAttributes;
27 import android.print.PrintAttributes.Margins;
28 import android.print.PrintAttributes.MediaSize;
29 import android.print.PrintAttributes.Resolution;
30 import android.print.PrintDocumentAdapter;
31 import android.print.PrintDocumentAdapter.LayoutResultCallback;
32 import android.print.PrintDocumentAdapter.WriteResultCallback;
33 import android.print.PrintDocumentInfo;
34 import android.print.PrinterCapabilitiesInfo;
35 import android.print.PrinterId;
36 import android.print.PrinterInfo;
37 import android.print.test.BasePrintTest;
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 
44 import androidx.test.runner.AndroidJUnit4;
45 
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.util.ArrayList;
50 import java.util.function.Consumer;
51 import java.util.function.Function;
52 
53 /**
54  * This test verifies changes to the printer capabilities are applied correctly.
55  */
56 @RunWith(AndroidJUnit4.class)
57 public class PrinterCapabilitiesTest extends BasePrintTest {
58     private static final String PRINTER_NAME = "Test printer";
59 
60     private static final Margins DEFAULT_MARGINS = new Margins(0, 0, 0, 0);
61     private static final PrintAttributes.Resolution RESOLUTION_300 =
62             new PrintAttributes.Resolution("300", "300", 300, 300);
63     private static final PrintAttributes.Resolution RESOLUTION_600 =
64             new PrintAttributes.Resolution("600", "600", 600, 600);
65     private static boolean sDefaultPrinterBeenSet;
66 
67     /**
68      * That that you cannot create illegal PrinterCapabilityInfos.
69      *
70      * @throws Exception If anything is unexpected
71      */
72     @Test
illegalPrinterCapabilityInfos()73     public void illegalPrinterCapabilityInfos() throws Exception {
74         final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
75                 createMockPrinterDiscoverySessionCallbacks(invocation -> {
76                     StubbablePrinterDiscoverySession session =
77                             ((PrinterDiscoverySessionCallbacks)
78                                     invocation.getMock()).getSession();
79 
80                     PrinterId printerId = session.getService().generatePrinterId(PRINTER_NAME);
81 
82                     // printerId need to be set
83                     assertException(() -> new PrinterCapabilitiesInfo.Builder(null),
84                             IllegalArgumentException.class);
85 
86                     // All capability fields (beside duplex) need to be initialized:
87                     // Test no color
88                     assertException(() ->
89                                     (new PrinterCapabilitiesInfo.Builder(printerId))
90                                             .setMinMargins(DEFAULT_MARGINS)
91                                             .addMediaSize(MediaSize.ISO_A4, true)
92                                             .addResolution(RESOLUTION_300, true).build(),
93                             IllegalStateException.class);
94                     // Test bad colors
95                     assertException(() ->
96                                     (new PrinterCapabilitiesInfo.Builder(printerId))
97                                             .setColorModes(0xffff,
98                                                     PrintAttributes.COLOR_MODE_MONOCHROME),
99                             IllegalArgumentException.class);
100                     // Test bad duplex mode
101                     assertException(() ->
102                                     (new PrinterCapabilitiesInfo.Builder(printerId))
103                                             .setDuplexModes(0xffff,
104                                                     PrintAttributes.DUPLEX_MODE_NONE),
105                             IllegalArgumentException.class);
106                     // Test no mediasize
107                     assertException(() ->
108                                     (new PrinterCapabilitiesInfo.Builder(printerId))
109                                             .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
110                                                     PrintAttributes.COLOR_MODE_COLOR)
111                                             .setMinMargins(DEFAULT_MARGINS)
112                                             .addResolution(RESOLUTION_300, true).build(),
113                             IllegalStateException.class);
114                     // Test no default mediasize
115                     assertException(() ->
116                                     (new PrinterCapabilitiesInfo.Builder(printerId))
117                                             .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
118                                                     PrintAttributes.COLOR_MODE_COLOR)
119                                             .setMinMargins(DEFAULT_MARGINS)
120                                             .addMediaSize(MediaSize.ISO_A4, false)
121                                             .addResolution(RESOLUTION_300, true).build(),
122                             IllegalStateException.class);
123                     // Test two default mediasizes
124                     assertException(() ->
125                                     (new PrinterCapabilitiesInfo.Builder(printerId))
126                                             .addMediaSize(MediaSize.ISO_A4, true)
127                                             .addMediaSize(MediaSize.ISO_A5, true),
128                             IllegalArgumentException.class);
129                     // Test no resolution
130                     assertException(() ->
131                                     (new PrinterCapabilitiesInfo.Builder(printerId))
132                                             .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
133                                                     PrintAttributes.COLOR_MODE_COLOR)
134                                             .setMinMargins(DEFAULT_MARGINS)
135                                             .addMediaSize(MediaSize.ISO_A4, true).build(),
136                             IllegalStateException.class);
137                     // Test no default resolution
138                     assertException(() ->
139                                     (new PrinterCapabilitiesInfo.Builder(printerId))
140                                             .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
141                                                     PrintAttributes.COLOR_MODE_COLOR)
142                                             .setMinMargins(DEFAULT_MARGINS)
143                                             .addMediaSize(MediaSize.ISO_A4, true)
144                                             .addResolution(RESOLUTION_300, false).build(),
145                             IllegalStateException.class);
146                     // Test two default resolutions
147                     assertException(() ->
148                                     (new PrinterCapabilitiesInfo.Builder(printerId))
149                                             .addResolution(RESOLUTION_300, true)
150                                             .addResolution(RESOLUTION_600, true),
151                             IllegalArgumentException.class);
152 
153                     onPrinterDiscoverySessionCreateCalled();
154                     return null;
155                 }, null, null, null, null, null, invocation -> {
156                     onPrinterDiscoverySessionDestroyCalled();
157                     return null;
158                 });
159 
160         // Create the service callbacks for the first print service.
161         PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
162                 invocation -> firstSessionCallbacks, null, null);
163 
164         // Configure the print services.
165         FirstPrintService.setCallbacks(firstServiceCallbacks);
166         SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
167 
168         PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(null, null, null);
169 
170         // Start printing.
171         print(adapter);
172 
173         waitForPrinterDiscoverySessionCreateCallbackCalled();
174 
175         getActivity().finish();
176 
177         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
178     }
179 
180     /**
181      * That that you can use all sane legal PrinterCapabilityInfos.
182      *
183      * @throws Exception If anything is unexpected
184      */
185     @Test
sanePrinterCapabilityInfos()186     public void sanePrinterCapabilityInfos() throws Exception {
187         final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
188                 createMockPrinterDiscoverySessionCallbacks(invocation -> {
189                     StubbablePrinterDiscoverySession session =
190                             ((PrinterDiscoverySessionCallbacks)
191                                     invocation.getMock()).getSession();
192 
193                     MediaSize[] mediaSizes = {MediaSize.ISO_A0, MediaSize.ISO_A0,
194                             MediaSize.ISO_A1};
195                     Resolution[] resolutions = {RESOLUTION_300, RESOLUTION_300,
196                             RESOLUTION_600};
197                     int[] colorModes = {PrintAttributes.COLOR_MODE_MONOCHROME,
198                             PrintAttributes.COLOR_MODE_COLOR};
199                     int[] duplexModes = {PrintAttributes.DUPLEX_MODE_NONE,
200                             PrintAttributes.DUPLEX_MODE_LONG_EDGE,
201                             PrintAttributes.DUPLEX_MODE_SHORT_EDGE};
202 
203                     ArrayList<PrinterInfo> printers = new ArrayList<>();
204                     for (int mediaSizeIndex = 1; mediaSizeIndex < mediaSizes.length;
205                          mediaSizeIndex++) {
206                         for (int resolutionIndex = 1; resolutionIndex < mediaSizes.length;
207                              resolutionIndex++) {
208                             for (int colorIndex = 1; colorIndex < colorModes.length;
209                                  colorIndex++) {
210                                 for (int duplexIndex = 1; duplexIndex < duplexModes.length;
211                                      duplexIndex++) {
212                                     PrinterId printerId = session.getService()
213                                             .generatePrinterId(Integer.valueOf(printers.size())
214                                                     .toString());
215 
216                                     // Setup capabilities
217                                     PrinterCapabilitiesInfo.Builder b =
218                                             new PrinterCapabilitiesInfo.Builder(printerId);
219 
220                                     for (int i = 0; i < mediaSizeIndex; i++) {
221                                         b.addMediaSize(mediaSizes[i], i == mediaSizeIndex - 1);
222                                     }
223 
224                                     for (int i = 0; i < resolutionIndex; i++) {
225                                         b.addResolution(resolutions[i],
226                                                 i == resolutionIndex - 1);
227                                     }
228 
229                                     int allColors = 0;
230                                     for (int i = 0; i < colorIndex; i++) {
231                                         allColors |= colorModes[i];
232                                     }
233                                     b.setColorModes(allColors, colorModes[colorIndex - 1]);
234 
235                                     int allDuplexModes = 0;
236                                     for (int i = 0; i < duplexIndex; i++) {
237                                         allDuplexModes |= duplexModes[i];
238                                     }
239                                     b.setDuplexModes(allDuplexModes,
240                                             duplexModes[duplexIndex - 1]);
241 
242                                     b.setMinMargins(DEFAULT_MARGINS);
243 
244                                     // Create printer
245                                     PrinterInfo printer = (new PrinterInfo.Builder(printerId,
246                                             Integer.valueOf(printers.size()).toString(),
247                                             PrinterInfo.STATUS_IDLE)).setCapabilities(b.build())
248                                             .build();
249 
250                                     // Verify capabilities
251                                     PrinterCapabilitiesInfo cap = printer.getCapabilities();
252 
253                                     assertEquals(mediaSizeIndex, cap.getMediaSizes().size());
254                                     assertEquals(mediaSizes[mediaSizeIndex - 1],
255                                             cap.getDefaults().getMediaSize());
256                                     for (int i = 0; i < mediaSizeIndex; i++) {
257                                         assertTrue(cap.getMediaSizes().contains(mediaSizes[i]));
258                                     }
259 
260                                     assertEquals(resolutionIndex, cap.getResolutions().size());
261                                     assertEquals(resolutions[resolutionIndex - 1],
262                                             cap.getDefaults().getResolution());
263                                     for (int i = 0; i < resolutionIndex; i++) {
264                                         assertTrue(cap.getResolutions().contains(resolutions[i]));
265                                     }
266 
267                                     assertEquals(allColors, cap.getColorModes());
268                                     assertEquals(colorModes[colorIndex - 1],
269                                             cap.getDefaults().getColorMode());
270 
271                                     assertEquals(allDuplexModes, cap.getDuplexModes());
272                                     assertEquals(duplexModes[duplexIndex - 1],
273                                             cap.getDefaults().getDuplexMode());
274 
275                                     assertEquals(DEFAULT_MARGINS, cap.getMinMargins());
276 
277                                     // Add printer
278                                     printers.add(printer);
279                                 }
280                             }
281                         }
282                     }
283 
284                     session.addPrinters(printers);
285 
286                     onPrinterDiscoverySessionCreateCalled();
287                     return null;
288                 }, null, null, null, null, null, invocation -> {
289                     onPrinterDiscoverySessionDestroyCalled();
290                     return null;
291                 });
292 
293         // Create the service callbacks for the first print service.
294         PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
295                 invocation -> firstSessionCallbacks, null, null);
296 
297         // Configure the print services.
298         FirstPrintService.setCallbacks(firstServiceCallbacks);
299         SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
300 
301         PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(null, null, null);
302 
303         // Start printing.
304         print(adapter);
305 
306         waitForPrinterDiscoverySessionCreateCallbackCalled();
307 
308         mPrintHelper.cancelPrinting();
309 
310         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
311     }
312 
313     /**
314      * Base test that performs a print operation with a give PrinterCapabilityInfo and run a test
315      * function before finishing.
316      *
317      * @throws Exception
318      */
testPrinterCapabilityInfo(final Function<PrinterId, PrinterCapabilitiesInfo> capBuilder, Consumer<PrintAttributes> test)319     private void testPrinterCapabilityInfo(final Function<PrinterId, PrinterCapabilitiesInfo>
320             capBuilder, Consumer<PrintAttributes> test) throws Exception {
321         final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
322                 createMockPrinterDiscoverySessionCallbacks(invocation -> {
323                     StubbablePrinterDiscoverySession session =
324                             ((PrinterDiscoverySessionCallbacks)
325                                     invocation.getMock()).getSession();
326 
327                     PrinterId printerId = session.getService()
328                             .generatePrinterId(PRINTER_NAME);
329 
330                     ArrayList<PrinterInfo> printers = new ArrayList<>();
331                     printers.add((new PrinterInfo.Builder(printerId, PRINTER_NAME,
332                             PrinterInfo.STATUS_IDLE))
333                             .setCapabilities(capBuilder.apply(printerId)).build());
334 
335                     session.addPrinters(printers);
336 
337                     onPrinterDiscoverySessionCreateCalled();
338                     return null;
339                 }, null, null, null, null, null, invocation -> {
340                     onPrinterDiscoverySessionDestroyCalled();
341                     return null;
342                 });
343 
344         // Create the service callbacks for the first print service.
345         PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
346                 invocation -> firstSessionCallbacks, null, null);
347 
348         // Configure the print services.
349         FirstPrintService.setCallbacks(firstServiceCallbacks);
350         SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
351 
352         final PrintAttributes[] layoutAttributes = new PrintAttributes[1];
353 
354         PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
355                 invocation -> {
356                     LayoutResultCallback callback = (LayoutResultCallback) invocation
357                             .getArguments()[3];
358                     PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
359                             .setPageCount(1)
360                             .build();
361                     layoutAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
362 
363                     callback.onLayoutFinished(info, true);
364                     return null;
365                 },
366                 invocation -> {
367                     Object[] args = invocation.getArguments();
368                     PageRange[] pages = (PageRange[]) args[0];
369                     ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
370                     WriteResultCallback callback = (WriteResultCallback) args[3];
371 
372                     writeBlankPages(layoutAttributes[0], fd, pages[0].getStart(),
373                             pages[0].getEnd());
374                     fd.close();
375 
376                     callback.onWriteFinished(pages);
377                     return null;
378                 }, null);
379 
380         // Start printing.
381         print(adapter);
382 
383         // make sure that options does not crash
384         openPrintOptions();
385 
386         if (!sDefaultPrinterBeenSet) {
387             // Select printer under test
388             selectPrinter(PRINTER_NAME);
389         }
390 
391         mPrintHelper.submitPrintJob();
392 
393         if (!sDefaultPrinterBeenSet) {
394             answerPrintServicesWarning(true);
395             sDefaultPrinterBeenSet = true;
396         }
397 
398         test.accept(layoutAttributes[0]);
399 
400         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
401     }
402 
403     /**
404      * That that you use a default color that is not in the allowed colors. This is allowed because
405      * of historical reasons.
406      *
407      * @throws Exception If anything is unexpected
408      */
409     @Test
invalidDefaultColor()410     public void invalidDefaultColor() throws Exception {
411         testPrinterCapabilityInfo(
412                 (printerId) -> (new PrinterCapabilitiesInfo.Builder(printerId))
413                         .addMediaSize(MediaSize.ISO_A4, true)
414                         .addResolution(RESOLUTION_300, true)
415                         .setColorModes(PrintAttributes.COLOR_MODE_MONOCHROME,
416                                 PrintAttributes.COLOR_MODE_COLOR).build(),
417                 (layoutAttributes) -> assertEquals(layoutAttributes.getColorMode(),
418                         PrintAttributes.COLOR_MODE_MONOCHROME));
419     }
420 
421     /**
422      * That that you use a default duplex mode that is not in the allowed duplex modes. This is
423      * allowed because of historical reasons.
424      *
425      * @throws Exception If anything is unexpected
426      */
427     @Test
invalidDefaultDuplexMode()428     public void invalidDefaultDuplexMode() throws Exception {
429         testPrinterCapabilityInfo(
430                 (printerId) -> (new PrinterCapabilitiesInfo.Builder(printerId))
431                         .addMediaSize(MediaSize.ISO_A4, true)
432                         .addResolution(RESOLUTION_300, true)
433                         .setColorModes(PrintAttributes.COLOR_MODE_MONOCHROME,
434                                 PrintAttributes.COLOR_MODE_MONOCHROME)
435                         .setDuplexModes(PrintAttributes.DUPLEX_MODE_LONG_EDGE
436                                 | PrintAttributes.DUPLEX_MODE_NONE,
437                                 PrintAttributes.DUPLEX_MODE_SHORT_EDGE).build(),
438                 (layoutAttributes) -> assertTrue(layoutAttributes.getDuplexMode() ==
439                         PrintAttributes.DUPLEX_MODE_LONG_EDGE || layoutAttributes.getDuplexMode() ==
440                         PrintAttributes.DUPLEX_MODE_NONE));
441     }
442 }
443