1 /*
2  * Copyright (C) 2015 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 org.junit.Assert.assertEquals;
20 
21 import android.os.ParcelFileDescriptor;
22 import android.print.PageRange;
23 import android.print.PrintAttributes;
24 import android.print.PrintAttributes.Margins;
25 import android.print.PrintAttributes.MediaSize;
26 import android.print.PrintAttributes.Resolution;
27 import android.print.PrintDocumentAdapter;
28 import android.print.PrintDocumentAdapter.LayoutResultCallback;
29 import android.print.PrintDocumentAdapter.WriteResultCallback;
30 import android.print.PrintDocumentInfo;
31 import android.print.PrinterCapabilitiesInfo;
32 import android.print.PrinterId;
33 import android.print.PrinterInfo;
34 import android.print.test.BasePrintTest;
35 import android.print.test.services.FirstPrintService;
36 import android.print.test.services.PrintServiceCallbacks;
37 import android.print.test.services.PrinterDiscoverySessionCallbacks;
38 import android.print.test.services.SecondPrintService;
39 import android.print.test.services.StubbablePrinterDiscoverySession;
40 import android.printservice.PrintJob;
41 import android.util.Log;
42 
43 import androidx.test.runner.AndroidJUnit4;
44 
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.List;
52 
53 /**
54  * Test that the print attributes are correctly propagated through the print framework
55  */
56 @RunWith(AndroidJUnit4.class)
57 public class PrintAttributesTest extends BasePrintTest {
58     private static final String LOG_TAG = "PrintAttributesTest";
59     private final String PRINTER_NAME = "Test printer";
60 
61     private final Margins[] MIN_MARGINS = {
62             new Margins(0, 0, 0, 0), new Margins(10, 10, 10, 10), new Margins(20, 20, 20, 20),
63     };
64 
65     private final MediaSize MEDIA_SIZES[] = {
66             MediaSize.ISO_A3, MediaSize.ISO_A4, MediaSize.ISO_A5
67     };
68 
69     private final int COLOR_MODES[] = {
70             PrintAttributes.COLOR_MODE_MONOCHROME, PrintAttributes.COLOR_MODE_COLOR
71     };
72 
73     private final int DUPLEX_MODES[] = {
74             PrintAttributes.DUPLEX_MODE_NONE, PrintAttributes.DUPLEX_MODE_LONG_EDGE,
75             PrintAttributes.DUPLEX_MODE_SHORT_EDGE
76     };
77 
78     private final Resolution RESOLUTIONS[] = {
79             new Resolution("300x300", "300x300", 300, 300),
80             new Resolution("600x600", "600x600", 600, 600),
81             new Resolution("1200x1200", "1200x1200", 1200, 1200)
82     };
83 
84     /**
85      * Stores the {@link PrintAttributes} passed to the layout method
86      */
87     private PrintAttributes mLayoutAttributes;
88     private static boolean sHasBeenSetup;
89 
90     /**
91      * Create a new {@link PrintAttributes} object with the given properties.
92      *
93      * All properties can be null/0 to remain unset.
94      *
95      * @param mediaSize {@link MediaSize} to use
96      * @param colorMode Color mode to use
97      * @param duplexMode Duplex mode to use
98      * @param resolution {@link Resolution} to use
99      *
100      * @return The newly created object or null if no properties are set
101      */
createAttributes(MediaSize mediaSize, int colorMode, int duplexMode, Resolution resolution)102     private PrintAttributes createAttributes(MediaSize mediaSize, int colorMode, int duplexMode,
103             Resolution resolution) {
104         if (mediaSize == null && colorMode == 0 && duplexMode == 0 && resolution == null) {
105             return null;
106         }
107 
108         PrintAttributes.Builder builder = new PrintAttributes.Builder();
109 
110         if (mediaSize != null) {
111             builder.setMediaSize(mediaSize);
112         }
113 
114         if (colorMode != 0) {
115             builder.setColorMode(colorMode);
116         }
117 
118         if (duplexMode != 0) {
119             builder.setDuplexMode(duplexMode);
120         }
121 
122         if (resolution != null) {
123             builder.setResolution(resolution);
124         }
125 
126         return builder.build();
127     }
128 
129     /**
130      * Create {@link PrinterDiscoverySessionCallbacks} with a single printer that has the given
131      * capabilities
132      *
133      * @param minMargins The minMargins of the printer
134      * @param mediaSizes The {@link MediaSize media sizes} supported by the printer
135      * @param defaultMediaSize The default {@link MediaSize}
136      * @param colorModes The color modes supported by the printer
137      * @param defaultColorMode The default color mode
138      * @param duplexModes The duplex modes supported by the printer
139      * @param defaultDuplexMode The default duplex mode
140      * @param resolutions The {@link Resolution resolutions} supported by the printer
141      * @param defaultResolution The default {@link Resolution} to use
142      *
143      * @return New {@link PrinterDiscoverySessionCallbacks} with a single printer that has the
144      *         given capabilities
145      */
createMockPrinterDiscoverySessionCallbacks( final Margins minMargins, final MediaSize mediaSizes[], final MediaSize defaultMediaSize, final int colorModes[], final int defaultColorMode, final int duplexModes[], final int defaultDuplexMode, final Resolution resolutions[], final Resolution defaultResolution)146     private PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
147             final Margins minMargins, final MediaSize mediaSizes[],
148             final MediaSize defaultMediaSize, final int colorModes[], final int defaultColorMode,
149             final int duplexModes[], final int defaultDuplexMode, final Resolution resolutions[],
150             final Resolution defaultResolution) {
151         return createMockPrinterDiscoverySessionCallbacks(invocation -> {
152             StubbablePrinterDiscoverySession session =
153                     ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
154 
155             if (session.getPrinters().isEmpty()) {
156                 List<PrinterInfo> printers = new ArrayList<>();
157                 PrinterId printerId = session.getService().generatePrinterId(PRINTER_NAME);
158 
159                 PrinterCapabilitiesInfo.Builder builder =
160                         new PrinterCapabilitiesInfo.Builder(printerId);
161 
162                 builder.setMinMargins(minMargins);
163 
164                 int mediaSizesLength = mediaSizes.length;
165                 for (int i = 0; i < mediaSizesLength; i++) {
166                     if (mediaSizes[i].equals(defaultMediaSize)) {
167                         builder.addMediaSize(mediaSizes[i], true);
168                     } else {
169                         builder.addMediaSize(mediaSizes[i], false);
170                     }
171                 }
172 
173                 int colorModesMask = 0;
174                 int colorModesLength = colorModes.length;
175                 for (int i = 0; i < colorModesLength; i++) {
176                     colorModesMask |= colorModes[i];
177                 }
178                 builder.setColorModes(colorModesMask, defaultColorMode);
179 
180                 int duplexModesMask = 0;
181                 int duplexModeLength = duplexModes.length;
182                 for (int i = 0; i < duplexModeLength; i++) {
183                     duplexModesMask |= duplexModes[i];
184                 }
185                 builder.setDuplexModes(duplexModesMask, defaultDuplexMode);
186 
187                 int resolutionsLength = resolutions.length;
188                 for (int i = 0; i < resolutionsLength; i++) {
189                     if (resolutions[i].equals(defaultResolution)) {
190                         builder.addResolution(resolutions[i], true);
191                     } else {
192                         builder.addResolution(resolutions[i], false);
193                     }
194                 }
195 
196                 PrinterInfo printer = new PrinterInfo.Builder(printerId, PRINTER_NAME,
197                         PrinterInfo.STATUS_IDLE).setCapabilities(builder.build()).build();
198                 printers.add(printer);
199 
200                 session.addPrinters(printers);
201             }
202             return null;
203         }, null, null, invocation -> null, null, null, invocation -> {
204             // Take a note onDestroy was called.
205             onPrinterDiscoverySessionDestroyCalled();
206             return null;
207         });
208     }
209 
210     /**
211      * Create dummy {@link PrintServiceCallbacks}
212      *
213      * This is needed to as the print framework is trying to talk to any printer even if is not set
214      * up.
215      *
216      * @return Dummy {@link PrintServiceCallbacks}
217      */
createDummyMockPrintServiceCallbacks()218     private PrintServiceCallbacks createDummyMockPrintServiceCallbacks() {
219         return createMockPrintServiceCallbacks(null, null, null);
220     }
221 
222     /**
223      * Create a {@link PrintDocumentAdapter} that serves empty pages
224      *
225      * @return A new {@link PrintDocumentAdapter}
226      */
createMockPrintDocumentAdapter()227     private PrintDocumentAdapter createMockPrintDocumentAdapter() {
228         return createMockPrintDocumentAdapter(
229                 invocation -> {
230                     mLayoutAttributes = (PrintAttributes) invocation.getArguments()[1];
231                     LayoutResultCallback callback =
232                             (LayoutResultCallback) invocation.getArguments()[3];
233                     PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
234                             .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
235                             .setPageCount(1)
236                             .build();
237                     callback.onLayoutFinished(info, false);
238                     // Mark layout was called.
239                     onLayoutCalled();
240                     return null;
241                 }, invocation -> {
242                     Object[] args = invocation.getArguments();
243                     PageRange[] pages = (PageRange[]) args[0];
244                     ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
245                     WriteResultCallback callback = (WriteResultCallback) args[3];
246                     writeBlankPages(mLayoutAttributes, fd, pages[0].getStart(),
247                             pages[0].getEnd());
248                     fd.close();
249                     callback.onWriteFinished(pages);
250                     // Mark write was called.
251                     onWriteCalled();
252                     return null;
253                 }, invocation -> {
254                     // Mark finish was called.
255                     onFinishCalled();
256                     return null;
257                 });
258     }
259 
260     /**
261      * Set up a single printer with the given capabilities
262      *
263      * @param minMargins The minMargins of the printer
264      * @param mediaSizes The {@link MediaSize media sizes} supported by the printer
265      * @param defaultMediaSize The default {@link MediaSize}
266      * @param colorModes The color modes supported by the printer
267      * @param defaultColorMode The default color mode
268      * @param duplexModes The duplex modes supported by the printer
269      * @param defaultDuplexMode The default duplex mode
270      * @param resolutions The {@link Resolution resolutions} supported by the printer
271      * @param defaultResolution The default {@link Resolution} to use
272      *
273      * @return A {@link PrintDocumentAdapter} that can be used for the new printer
274      */
275     private PrintDocumentAdapter setUpPrinter(Margins minMargins, MediaSize mediaSizes[],
276             MediaSize defaultMediaSize, int colorModes[], int defaultColorMode, int duplexModes[],
277             int defaultDuplexMode, Resolution resolutions[], Resolution defaultResolution) {
278         final PrinterDiscoverySessionCallbacks sessionCallbacks =
279                 createMockPrinterDiscoverySessionCallbacks(minMargins, mediaSizes,
280                         defaultMediaSize, colorModes, defaultColorMode, duplexModes,
281                         defaultDuplexMode, resolutions, defaultResolution);
282 
283         PrintServiceCallbacks serviceCallbacks = createMockPrintServiceCallbacks(
284                 invocation -> sessionCallbacks,
285                 invocation -> {
286                     PrintJob printJob = (PrintJob) invocation.getArguments()[0];
287                     // We pretend the job is handled immediately.
288                     printJob.complete();
289                     return null;
290                 }, null);
291 
292         // Configure the print services.
293         FirstPrintService.setCallbacks(serviceCallbacks);
294 
295         // We need to set up the second print service too, otherwise we get a null pointer in the
296         // print framework
297         SecondPrintService.setCallbacks(createDummyMockPrintServiceCallbacks());
298 
299         // Create a print adapter that respects the print contract.
300         return createMockPrintDocumentAdapter();
301     }
302 
303     /**
304      * Check if a value is in an array.
305      *
306      * To be use instead of Arrays.asList(array).contains(value) for ints.
307      *
308      * @param array The array the value might be in
309      * @param value The value to search for
310      *
311      * @return true iff the value is in the array
312      */
313     private boolean isInArray(final int array[], int value) {
314         int arrayLength = array.length;
315         for (int i = 0; i < arrayLength; i++) {
316             if (array[i] == value) {
317                 return true;
318             }
319         }
320 
321         return false;
322     }
323 
324     @Before
325     public void setUpServicesAndAdapter() throws Throwable {
326         if (!sHasBeenSetup) {
327             // Set up printer with supported and default attributes
328             PrintDocumentAdapter adapter =
329                     setUpPrinter(MIN_MARGINS[0], MEDIA_SIZES, MEDIA_SIZES[0], COLOR_MODES,
330                             COLOR_MODES[0], DUPLEX_MODES, DUPLEX_MODES[0], RESOLUTIONS,
331                             RESOLUTIONS[0]);
332 
333             Log.d(LOG_TAG, "makeDefaultPrinter");
334             // Make printer default. This is necessary as a different default printer might pre-select
335             // its default attributes and thereby overrides the defaults of the tested printer.
336             makeDefaultPrinter(adapter, PRINTER_NAME);
337 
338             sHasBeenSetup = true;
339         }
340 
341         resetCounters();
342     }
343 
344     /**
345      * Flexible base test for all print attribute tests.
346      *
347      * Asserts that the default and suggested attributes are properly honored by the print
348      * framework.
349      *
350      * @param minMargins The minMargins of the printer
351      * @param mediaSizes The {@link MediaSize media sizes} supported by the printer
352      * @param defaultMediaSize The default {@link MediaSize}
353      * @param colorModes The color modes supported by the printer
354      * @param defaultColorMode The default color mode
355      * @param duplexModes The duplex modes supported by the printer
356      * @param defaultDuplexMode The default duplex mode
357      * @param resolutions The {@link Resolution resolutions} supported by the printer
358      * @param defaultResolution The default {@link Resolution} to use
359      * @param suggestedMediaSize The suggested {@link MediaSize} for the print job
360      * @param suggestedColorMode The suggested color mode for the print job
361      * @param suggestedDuplexMode The suggested duplex mode for the print job
362      * @param suggestedResolution The suggested resolution for the print job
363      *
364      * @throws Exception If anything is unexpected
365      */
366     private void baseTest(Margins minMargins, MediaSize mediaSizes[],
367             MediaSize defaultMediaSize, MediaSize suggestedMediaSize, int colorModes[],
368             int defaultColorMode, int suggestedColorMode, int duplexModes[],
369             int defaultDuplexMode, int suggestedDuplexMode, Resolution resolutions[],
370             Resolution defaultResolution, Resolution suggestedResolution) throws Exception {
371         PrintDocumentAdapter adapter =
372                 setUpPrinter(minMargins, mediaSizes, defaultMediaSize, colorModes, defaultColorMode,
373                         duplexModes, defaultDuplexMode, resolutions, defaultResolution);
374 
375         // Select suggested attributes
376         PrintAttributes suggestedAttributes = createAttributes(suggestedMediaSize,
377                 suggestedColorMode, suggestedDuplexMode, suggestedResolution);
378 
379         // Start print action and wait for layout, the result is stored in #layoutAttributes,
380         // @see createMockPrintDocumentAdapter
381         Log.d(LOG_TAG, "print");
382         print(adapter, suggestedAttributes);
383         Log.d(LOG_TAG, "waitForWriteAdapterCallback");
384         waitForWriteAdapterCallback(1);
385         Log.d(LOG_TAG, "clickPrintButton");
386         mPrintHelper.submitPrintJob();
387         Log.d(LOG_TAG, "waitForPrinterDiscoverySessionDestroyCallbackCalled");
388         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
389 
390         // It does not make sense to suggest minMargins, hence the print framework always picks
391         // the one set up for the printer.
392         assertEquals("Min margins not as expected", minMargins, mLayoutAttributes.getMinMargins());
393 
394         // Verify that the attributes are honored properly
395         if (suggestedMediaSize != null && Arrays.asList(mediaSizes).contains(suggestedMediaSize)) {
396             assertEquals("Media size not as suggested", suggestedMediaSize,
397                     mLayoutAttributes.getMediaSize());
398         } else {
399             assertEquals("Media size not default", defaultMediaSize,
400                     mLayoutAttributes.getMediaSize());
401         }
402 
403         if (suggestedColorMode != 0 && isInArray(colorModes, suggestedColorMode)) {
404             assertEquals("Color mode not as suggested", suggestedColorMode,
405                     mLayoutAttributes.getColorMode());
406         } else {
407             assertEquals("Color mode not default", defaultColorMode,
408                     mLayoutAttributes.getColorMode());
409         }
410 
411         if (suggestedDuplexMode != 0 && isInArray(duplexModes, suggestedDuplexMode)) {
412             assertEquals("Duplex mode not as suggested", suggestedDuplexMode,
413                     mLayoutAttributes.getDuplexMode());
414         } else {
415             assertEquals("Duplex mode not default", defaultDuplexMode,
416                     mLayoutAttributes.getDuplexMode());
417         }
418 
419         if (suggestedResolution != null
420                 && Arrays.asList(resolutions).contains(suggestedResolution)) {
421             assertEquals("Resolution not as suggested", suggestedResolution,
422                     mLayoutAttributes.getResolution());
423         } else {
424             assertEquals("Resolution not default", defaultResolution,
425                     mLayoutAttributes.getResolution());
426         }
427     }
428 
429     /**
430      * Test that attributes are as expected if the default attributes match the suggested ones.
431      *
432      * This test sets the default and suggested attributes to the first selection.
433      *
434      * @throws Exception If anything is unexpected
435      */
436     @Test
437     public void defaultMatchesSuggested0() throws Exception {
438         //       available     default          suggestion
439         baseTest(              MIN_MARGINS[0],
440                  MEDIA_SIZES,  MEDIA_SIZES[0],  MEDIA_SIZES[0],
441                  COLOR_MODES,  COLOR_MODES[0],  COLOR_MODES[0],
442                  DUPLEX_MODES, DUPLEX_MODES[0], DUPLEX_MODES[0],
443                  RESOLUTIONS,  RESOLUTIONS[0],  RESOLUTIONS[0]);
444     }
445 
446     /**
447      * Test that attributes are as expected if the default attributes match the suggested ones.
448      *
449      * This test sets the default and suggested attributes to the second selection.
450      *
451      * @throws Exception If anything is unexpected
452      */
453     @Test
454     public void defaultMatchesSuggested1() throws Exception {
455         //       available     default          suggestion
456         baseTest(              MIN_MARGINS[1],
457                  MEDIA_SIZES,  MEDIA_SIZES[1],  MEDIA_SIZES[1],
458                  COLOR_MODES,  COLOR_MODES[1],  COLOR_MODES[1],
459                  DUPLEX_MODES, DUPLEX_MODES[1], DUPLEX_MODES[1],
460                  RESOLUTIONS,  RESOLUTIONS[1],  RESOLUTIONS[1]);
461     }
462 
463     /**
464      * Test that attributes are as expected if the default attributes match the suggested ones.
465      *
466      * This test sets the default and suggested attributes to the third selection.
467      *
468      * @throws Exception If anything is unexpected
469      */
470     @Test
471     public void defaultMatchesSuggested2() throws Exception {
472         //       available     default          suggestion
473         baseTest(              MIN_MARGINS[2],
474                  MEDIA_SIZES,  MEDIA_SIZES[2],  MEDIA_SIZES[2],
475                  // There are only two color modes, hence pick [1]
476                  COLOR_MODES,  COLOR_MODES[1],  COLOR_MODES[1],
477                  DUPLEX_MODES, DUPLEX_MODES[2], DUPLEX_MODES[2],
478                  RESOLUTIONS,  RESOLUTIONS[2],  RESOLUTIONS[2]);
479     }
480 
481     /**
482      * Test that attributes are as expected if the no suggestion is given.
483      *
484      * This test sets the default attributes to the first selection.
485      *
486      * @throws Exception If anything is unexpected
487      */
488     @Test
489     public void noSuggestion0() throws Exception {
490         //       available     default          suggestion
491         baseTest(              MIN_MARGINS[0],
492                  MEDIA_SIZES,  MEDIA_SIZES[0],  null,
493                  COLOR_MODES,  COLOR_MODES[0],  0,
494                  DUPLEX_MODES, DUPLEX_MODES[0], 0,
495                  RESOLUTIONS,  RESOLUTIONS[0],  null);
496     }
497 
498     /**
499      * Test that attributes are as expected if the no suggestion is given.
500      *
501      * This test sets the default attributes to the second selection.
502      *
503      * @throws Exception If anything is unexpected
504      */
505     @Test
506     public void noSuggestion1() throws Exception {
507         //       available     default          suggestion
508         baseTest(              MIN_MARGINS[1],
509                  MEDIA_SIZES,  MEDIA_SIZES[1],  null,
510                  COLOR_MODES,  COLOR_MODES[1],  0,
511                  DUPLEX_MODES, DUPLEX_MODES[1], 0,
512                  RESOLUTIONS,  RESOLUTIONS[1],  null);
513     }
514 
515     /**
516      * Test that attributes are as expected if the no suggestion is given.
517      *
518      * This test sets the default attributes to the third selection.
519      *
520      * @throws Exception If anything is unexpected
521      */
522     @Test
523     public void noSuggestion2() throws Exception {
524         //       available     default          suggestion
525         baseTest(              MIN_MARGINS[2],
526                  MEDIA_SIZES,  MEDIA_SIZES[2],  null,
527                  // There are only two color modes, hence pick [1]
528                  COLOR_MODES,  COLOR_MODES[1],  0,
529                  DUPLEX_MODES, DUPLEX_MODES[2], 0,
530                  RESOLUTIONS,  RESOLUTIONS[2],  null);
531     }
532 
533     /**
534      * Test that attributes are as expected if only the {@link MediaSize} is suggested.
535      *
536      * This test sets the default attributes to the first selection, but the {@link MediaSize} is
537      * suggested to be the second selection.
538      *
539      * @throws Exception If anything is unexpected
540      */
541     @Test
542     public void mediaSizeSuggestion0() throws Exception {
543         //       available     default          suggestion
544         baseTest(              MIN_MARGINS[0],
545                  MEDIA_SIZES,  MEDIA_SIZES[0],  MEDIA_SIZES[1],
546                  COLOR_MODES,  COLOR_MODES[0],  0,
547                  DUPLEX_MODES, DUPLEX_MODES[0], 0,
548                  RESOLUTIONS,  RESOLUTIONS[0],  null);
549     }
550 
551     /**
552      * Test that attributes are as expected if only the {@link MediaSize} is suggested.
553      *
554      * This test sets the default attributes to the second selection, but the {@link MediaSize} is
555      * suggested to be the first selection.
556      *
557      * @throws Exception If anything is unexpected
558      */
559     @Test
560     public void mediaSizeSuggestion1() throws Exception {
561         //       available     default          suggestion
562         baseTest(              MIN_MARGINS[1],
563                  MEDIA_SIZES,  MEDIA_SIZES[1],  MEDIA_SIZES[0],
564                  COLOR_MODES,  COLOR_MODES[1],  0,
565                  DUPLEX_MODES, DUPLEX_MODES[1], 0,
566                  RESOLUTIONS,  RESOLUTIONS[1],  null);
567     }
568 
569     /**
570      * Test that attributes are as expected if only the duplex mode is suggested.
571      *
572      * This test sets the default attributes to the first selection, but the duplex mode is
573      * suggested to be the second selection.
574      *
575      * @throws Exception If anything is unexpected
576      */
577     @Test
578     public void duplexModeSuggestion0() throws Exception {
579         //       available     default          suggestion
580         baseTest(              MIN_MARGINS[0],
581                  MEDIA_SIZES,  MEDIA_SIZES[0],  null,
582                  COLOR_MODES,  COLOR_MODES[0],  0,
583                  DUPLEX_MODES, DUPLEX_MODES[0], DUPLEX_MODES[1],
584                  RESOLUTIONS,  RESOLUTIONS[0],  null);
585     }
586 
587     /**
588      * Test that attributes are as expected if only the duplex mode is suggested.
589      *
590      * This test sets the default attributes to the second selection, but the duplex mode is
591      * suggested to be the first selection.
592      *
593      * @throws Exception If anything is unexpected
594      */
595     @Test
596     public void duplexModeSuggestion1() throws Exception {
597         //       available     default          suggestion
598         baseTest(              MIN_MARGINS[1],
599                  MEDIA_SIZES,  MEDIA_SIZES[1],  null,
600                  COLOR_MODES,  COLOR_MODES[1],  0,
601                  DUPLEX_MODES, DUPLEX_MODES[1], DUPLEX_MODES[0],
602                  RESOLUTIONS,  RESOLUTIONS[1],  null);
603     }
604 
605     /**
606      * Test that attributes are as expected if all attributes are suggested and different from the
607      * default attributes.
608      *
609      * @throws Exception If anything is unexpected
610      */
611     @Test
612     public void suggestedDifferentFromDefault() throws Exception {
613         //       available     default          suggestion
614         baseTest(              MIN_MARGINS[0],
615                  MEDIA_SIZES,  MEDIA_SIZES[0],  MEDIA_SIZES[1],
616                  COLOR_MODES,  COLOR_MODES[0],  COLOR_MODES[1],
617                  DUPLEX_MODES, DUPLEX_MODES[0], DUPLEX_MODES[1],
618                  RESOLUTIONS,  RESOLUTIONS[0],  RESOLUTIONS[1]);
619     }
620 
621     /**
622      * Test that attributes are as expected if all attributes are suggested but all of them are not
623      * supported by the printer.
624      *
625      * @throws Exception If anything is unexpected
626      */
627     @Test
628     public void unsupportedSuggested() throws Exception {
629         //       available                               default          suggestion
630         baseTest(                                        MIN_MARGINS[0],
631                  Arrays.copyOfRange(MEDIA_SIZES, 0, 1),  MEDIA_SIZES[0],  MEDIA_SIZES[1],
632                  Arrays.copyOfRange(COLOR_MODES, 0, 1),  COLOR_MODES[0],  COLOR_MODES[1],
633                  Arrays.copyOfRange(DUPLEX_MODES, 0, 1), DUPLEX_MODES[0], DUPLEX_MODES[1],
634                  Arrays.copyOfRange(RESOLUTIONS, 0, 1),  RESOLUTIONS[0],  RESOLUTIONS[1]);
635     }
636 
637     /**
638      * Test that negative Margins do not cause issues in the print print spooler. Negative margins
639      * are allowed because of historical reasons.
640      *
641      * @throws Exception If anything is unexpected
642      */
643     @Test
644     public void negativeMargins() throws Exception {
645         //       available     default                          suggestion
646         baseTest(              new Margins(-10, -10, -10, -10),
647                  MEDIA_SIZES,  MEDIA_SIZES[1],                  null,
648                  COLOR_MODES,  COLOR_MODES[1],                  0,
649                  DUPLEX_MODES, DUPLEX_MODES[1],                 0,
650                  RESOLUTIONS,  RESOLUTIONS[1],                  null);
651     }
652 }
653