blob: 145b8353f6e4d3d01e373f20e8037283fbc60493 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.print.cts;
import static android.print.test.Utils.eventually;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.AppModeFull;
import android.print.PrintAttributes;
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
import android.print.PrintDocumentAdapter;
import android.print.PrintJobInfo;
import android.print.PrintManager;
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.print.test.BasePrintTest;
import android.print.test.services.CustomPrintOptionsActivity;
import android.print.test.services.FirstPrintService;
import android.print.test.services.PrintServiceCallbacks;
import android.print.test.services.PrinterDiscoverySessionCallbacks;
import android.print.test.services.SecondPrintService;
import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
/**
* Tests all possible states of print jobs.
*/
@RunWith(AndroidJUnit4.class)
public class PrintJobTest extends BasePrintTest {
private static final String PRINTER_NAME = "TestPrinter";
private final static String VALID_NULL_KEY = "validNullKey";
private final static String VALID_STRING_KEY = "validStringKey";
private final static String STRING_VALUE = "string value";
private final static String INVALID_STRING_KEY = "invalidStringKey";
private final static String VALID_INT_KEY = "validIntKey";
private final static int INT_VALUE = 23;
private final static String INVALID_INT_KEY = "invalidIntKey";
private final boolean testSuccess[] = new boolean[1];
/** The printer discovery session used in this test */
private static StubbablePrinterDiscoverySession sDiscoverySession;
private static boolean sHasBeenSetUp;
/**
* Create a mock {@link PrinterDiscoverySessionCallbacks} that discovers a simple test printer.
*
* @return The mock session callbacks
*/
private PrinterDiscoverySessionCallbacks createFirstMockPrinterDiscoverySessionCallbacks() {
return createMockPrinterDiscoverySessionCallbacks(invocation -> {
// Get the session.
sDiscoverySession = ((PrinterDiscoverySessionCallbacks) invocation.getMock())
.getSession();
if (sDiscoverySession.getPrinters().isEmpty()) {
PrinterId printerId =
sDiscoverySession.getService().generatePrinterId(PRINTER_NAME);
PrinterInfo.Builder printer = new PrinterInfo.Builder(
sDiscoverySession.getService().generatePrinterId(PRINTER_NAME),
PRINTER_NAME, PrinterInfo.STATUS_IDLE);
printer.setCapabilities(new PrinterCapabilitiesInfo.Builder(printerId)
.addMediaSize(MediaSize.ISO_A4, true)
.addResolution(new Resolution("300x300", "300dpi", 300, 300), true)
.setColorModes(PrintAttributes.COLOR_MODE_COLOR,
PrintAttributes.COLOR_MODE_COLOR)
.setMinMargins(new Margins(0, 0, 0, 0)).build());
ArrayList<PrinterInfo> printers = new ArrayList<>(1);
printers.add(printer.build());
sDiscoverySession.addPrinters(printers);
}
return null;
}, null, null, invocation -> null, null, null, invocation -> {
// Take a note onDestroy was called.
onPrinterDiscoverySessionDestroyCalled();
return null;
});
}
private interface PrintJobTestFn {
void onPrintJobQueued(PrintJob printJob) throws Throwable;
}
/**
* Create mock service callback for a session. Once the job is queued the test function is
* called.
*
* @param sessionCallbacks The callbacks of the session
* @param printJobTest test function to call
*/
private PrintServiceCallbacks createFirstMockPrinterServiceCallbacks(
final PrinterDiscoverySessionCallbacks sessionCallbacks,
final PrintJobTestFn printJobTest) {
return createMockPrintServiceCallbacks(
invocation -> sessionCallbacks, invocation -> {
PrintJob printJob = (PrintJob) invocation.getArguments()[0];
try {
printJobTest.onPrintJobQueued(printJob);
testSuccess[0] = true;
} catch (Throwable t) {
throw new RuntimeException(t);
}
onPrintJobQueuedCalled();
return null;
}, null);
}
/**
* Base test for the print job tests. Starts a print job and executes a testFn once the job is
* queued.
*
* @throws Exception If anything is unexpected.
*/
private void baseTest(PrintJobTestFn testFn)
throws Exception {
testSuccess[0] = false;
// Create the session of the printers that we will be checking.
PrinterDiscoverySessionCallbacks sessionCallbacks
= createFirstMockPrinterDiscoverySessionCallbacks();
// Create the service callbacks for the first print service.
PrintServiceCallbacks serviceCallbacks = createFirstMockPrinterServiceCallbacks(
sessionCallbacks, testFn);
// Configure the print services.
FirstPrintService.setCallbacks(serviceCallbacks);
// We don't use the second service, but we have to still configure it
SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
// Create a print adapter that respects the print contract.
PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
// Start printing.
print(adapter);
clickPrintButton();
// Wait for print job to be queued
waitForServiceOnPrintJobQueuedCallbackCalled(1);
// Wait for discovery session to be destroyed to isolate tests from each other
waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
if (!testSuccess[0]) {
throw new Exception("Did not succeed");
}
}
private static void checkState(PrintJob job, int state) throws Throwable {
eventually(() -> assertEquals(state, job.getInfo().getState()));
switch (state) {
case PrintJobInfo.STATE_QUEUED:
eventually(() -> assertTrue(job.isQueued()));
break;
case PrintJobInfo.STATE_STARTED:
eventually(() -> assertTrue(job.isStarted()));
break;
case PrintJobInfo.STATE_BLOCKED:
eventually(() -> assertTrue(job.isBlocked()));
break;
case PrintJobInfo.STATE_COMPLETED:
eventually(() -> assertTrue(job.isCompleted()));
break;
case PrintJobInfo.STATE_FAILED:
eventually(() -> assertTrue(job.isFailed()));
break;
case PrintJobInfo.STATE_CANCELED:
eventually(() -> assertTrue(job.isCancelled()));
break;
default:
// not reached
throw new IllegalArgumentException("Cannot check " + state);
}
}
@Before
public void setPrinter() throws Exception {
if (!sHasBeenSetUp) {
resetCounters();
PrinterDiscoverySessionCallbacks sessionCallbacks
= createFirstMockPrinterDiscoverySessionCallbacks();
// Create the service callbacks for the first print service.
PrintServiceCallbacks serviceCallbacks = createFirstMockPrinterServiceCallbacks(
sessionCallbacks, printJob -> { });
// Configure the print services.
FirstPrintService.setCallbacks(serviceCallbacks);
// We don't use the second service, but we have to still configure it
SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
// Create a print adapter that respects the print contract.
PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
makeDefaultPrinter(adapter, PRINTER_NAME);
sHasBeenSetUp = true;
}
resetCounters();
}
@Test
public void blockWithReason() throws Exception {
baseTest(printJob -> {
printJob.start();
checkState(printJob, PrintJobInfo.STATE_STARTED);
printJob.setStatus(R.string.testStr1);
eventually(() -> assertEquals(getActivity().getString(R.string.testStr1),
printJob.getInfo().getStatus(getActivity().getPackageManager())));
boolean success = printJob.block("test reason");
assertTrue(success);
checkState(printJob, PrintJobInfo.STATE_BLOCKED);
eventually(() -> assertEquals("test reason",
printJob.getInfo().getStatus(getActivity().getPackageManager())));
success = printJob.block("another reason");
assertFalse(success);
checkState(printJob, PrintJobInfo.STATE_BLOCKED);
eventually(() -> assertEquals("test reason",
printJob.getInfo().getStatus(getActivity().getPackageManager())));
printJob.setStatus(R.string.testStr2);
eventually(() -> assertEquals(getActivity().getString(R.string.testStr2),
printJob.getInfo().getStatus(getActivity().getPackageManager())));
});
}
@Test
public void failWithReason() throws Exception {
baseTest(printJob -> {
printJob.start();
checkState(printJob, PrintJobInfo.STATE_STARTED);
boolean success = printJob.fail("test reason");
assertTrue(success);
checkState(printJob, PrintJobInfo.STATE_FAILED);
eventually(() -> assertEquals("test reason",
printJob.getInfo().getStatus(getActivity().getPackageManager())));
success = printJob.fail("another reason");
assertFalse(success);
checkState(printJob, PrintJobInfo.STATE_FAILED);
eventually(() -> assertEquals("test reason",
printJob.getInfo().getStatus(getActivity().getPackageManager())));
});
}
@Test
public void tag() throws Exception {
baseTest(printJob -> {
// Default value should be null
assertNull(printJob.getTag());
printJob.setTag("testTag");
eventually(() -> assertEquals("testTag", printJob.getTag()));
printJob.setTag(null);
eventually(() -> assertNull(printJob.getTag()));
});
}
@AppModeFull(reason = "Print UI cannot resolve custom print options activity in a instant app")
@Test
public void advancedOption() throws Exception {
testSuccess[0] = false;
// Create the session of the printers that we will be checking.
PrinterDiscoverySessionCallbacks sessionCallbacks
= createFirstMockPrinterDiscoverySessionCallbacks();
// Create the service callbacks for the first print service.
PrintServiceCallbacks serviceCallbacks = createFirstMockPrinterServiceCallbacks(
sessionCallbacks, printJob -> {
assertTrue(printJob.hasAdvancedOption(VALID_STRING_KEY));
assertEquals(STRING_VALUE, printJob.getAdvancedStringOption(VALID_STRING_KEY));
assertFalse(printJob.hasAdvancedOption(INVALID_STRING_KEY));
assertNull(printJob.getAdvancedStringOption(INVALID_STRING_KEY));
assertTrue(printJob.hasAdvancedOption(VALID_INT_KEY));
assertEquals(INT_VALUE, printJob.getAdvancedIntOption(VALID_INT_KEY));
assertTrue(printJob.hasAdvancedOption(VALID_NULL_KEY));
assertNull(printJob.getAdvancedStringOption(VALID_NULL_KEY));
assertFalse(printJob.hasAdvancedOption(INVALID_INT_KEY));
assertEquals(0, printJob.getAdvancedIntOption(INVALID_INT_KEY));
assertNull(printJob.getAdvancedStringOption(VALID_INT_KEY));
assertEquals(0, printJob.getAdvancedIntOption(VALID_STRING_KEY));
});
final int[] callCount = new int[1];
CustomPrintOptionsActivity.setCallBack(
(printJob, printer) -> {
if (callCount[0] == 0) {
PrintJobInfo.Builder printJobBuilder = new PrintJobInfo.Builder(printJob);
try {
printJobBuilder.putAdvancedOption(null, STRING_VALUE);
throw new RuntimeException("Should not be able to use a null key");
} catch (NullPointerException e) {
// expected
}
// Second put overrides the first
printJobBuilder.putAdvancedOption(VALID_STRING_KEY, "something");
printJobBuilder.putAdvancedOption(VALID_STRING_KEY, STRING_VALUE);
printJobBuilder.putAdvancedOption(VALID_INT_KEY, "something");
printJobBuilder.putAdvancedOption(VALID_INT_KEY, INT_VALUE);
printJobBuilder.putAdvancedOption(VALID_NULL_KEY, null);
// Rotate the media size to force adapter to write again
PrintAttributes.Builder attributeBuilder = new PrintAttributes.Builder();
attributeBuilder.setMediaSize(printJob.getAttributes().getMediaSize()
.asLandscape());
attributeBuilder.setResolution(printJob.getAttributes().getResolution());
attributeBuilder.setDuplexMode(printJob.getAttributes().getDuplexMode());
attributeBuilder.setColorMode(printJob.getAttributes().getColorMode());
attributeBuilder.setMinMargins(printJob.getAttributes().getMinMargins());
printJobBuilder.setAttributes(attributeBuilder.build());
return printJobBuilder.build();
} else {
// Check that options are readable
assertTrue(printJob.hasAdvancedOption(VALID_STRING_KEY));
assertEquals(STRING_VALUE,
printJob.getAdvancedStringOption(VALID_STRING_KEY));
assertFalse(printJob.hasAdvancedOption(INVALID_STRING_KEY));
assertNull(printJob.getAdvancedStringOption(INVALID_STRING_KEY));
assertTrue(printJob.hasAdvancedOption(VALID_INT_KEY));
assertEquals(INT_VALUE, printJob.getAdvancedIntOption(VALID_INT_KEY));
assertTrue(printJob.hasAdvancedOption(VALID_NULL_KEY));
assertNull(printJob.getAdvancedStringOption(VALID_NULL_KEY));
assertFalse(printJob.hasAdvancedOption(INVALID_INT_KEY));
assertEquals(0, printJob.getAdvancedIntOption(INVALID_INT_KEY));
assertNull(printJob.getAdvancedStringOption(VALID_INT_KEY));
assertEquals(0, printJob.getAdvancedIntOption(VALID_STRING_KEY));
return null;
}
});
// Configure the print services.
FirstPrintService.setCallbacks(serviceCallbacks);
// We don't use the second service, but we have to still configure it
SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
// Create a print adapter that respects the print contract.
PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
// Start printing.
print(adapter, "advancedOption");
waitForWriteAdapterCallback(1);
openPrintOptions();
openCustomPrintOptions();
waitForWriteAdapterCallback(2);
callCount[0]++;
// The advanced option should not be readable from the activity
getActivity().getSystemService(PrintManager.class).getPrintJobs().stream()
.filter(printJob -> printJob.getInfo().getLabel().equals("advancedOption"))
.forEach(printJob -> {
assertFalse(printJob.getInfo().hasAdvancedOption(VALID_STRING_KEY));
assertEquals(null,
printJob.getInfo().getAdvancedStringOption(VALID_STRING_KEY));
assertFalse(printJob.getInfo().hasAdvancedOption(INVALID_STRING_KEY));
assertNull(printJob.getInfo().getAdvancedStringOption(INVALID_STRING_KEY));
assertFalse(printJob.getInfo().hasAdvancedOption(VALID_INT_KEY));
assertEquals(0, printJob.getInfo().getAdvancedIntOption(VALID_INT_KEY));
assertFalse(printJob.getInfo().hasAdvancedOption(VALID_NULL_KEY));
assertNull(printJob.getInfo().getAdvancedStringOption(VALID_NULL_KEY));
assertFalse(printJob.getInfo().hasAdvancedOption(INVALID_INT_KEY));
assertEquals(0, printJob.getInfo().getAdvancedIntOption(INVALID_INT_KEY));
assertNull(printJob.getInfo().getAdvancedStringOption(VALID_INT_KEY));
assertEquals(0, printJob.getInfo().getAdvancedIntOption(VALID_STRING_KEY));
});
openCustomPrintOptions();
clickPrintButton();
// Wait for print job to be queued
waitForServiceOnPrintJobQueuedCallbackCalled(1);
// Wait for discovery session to be destroyed to isolate tests from each other
waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
if (!testSuccess[0]) {
throw new Exception("Did not succeed");
}
}
@Test
public void other() throws Exception {
baseTest(printJob -> {
assertNotNull(printJob.getDocument());
assertNotNull(printJob.getId());
});
}
@Test
public void setStatus() throws Exception {
baseTest(printJob -> {
printJob.start();
printJob.setStatus(R.string.testStr1);
eventually(() -> assertEquals(getActivity().getString(R.string.testStr1),
printJob.getInfo().getStatus(getActivity().getPackageManager())));
printJob.setStatus("testStr3");
eventually(() -> assertEquals("testStr3",
printJob.getInfo().getStatus(getActivity().getPackageManager())));
printJob.setStatus(R.string.testStr2);
eventually(() -> assertEquals(getActivity().getString(R.string.testStr2),
printJob.getInfo().getStatus(getActivity().getPackageManager())));
printJob.setStatus(null);
eventually(() -> assertNull(
printJob.getInfo().getStatus(getActivity().getPackageManager())));
printJob.block("testStr4");
eventually(() -> assertEquals("testStr4",
printJob.getInfo().getStatus(getActivity().getPackageManager())));
printJob.setStatus(R.string.testStr2);
eventually(() -> assertEquals(getActivity().getString(R.string.testStr2),
printJob.getInfo().getStatus(getActivity().getPackageManager())));
printJob.setStatus(0);
eventually(() -> assertNull(
printJob.getInfo().getStatus(getActivity().getPackageManager())));
printJob.setStatus("testStr3");
eventually(() -> assertEquals("testStr3",
printJob.getInfo().getStatus(getActivity().getPackageManager())));
printJob.setStatus(-1);
eventually(() -> assertNull(
printJob.getInfo().getStatus(getActivity().getPackageManager())));
});
}
}