New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8262731: [macOS] Exception from "Printable.print" is swallowed during "PrinterJob.print" #4036
Changes from 2 commits
db2ce06
4705151
746dc0c
056734b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/* | ||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License version 2 only, as | ||
* published by the Free Software Foundation. | ||
* | ||
* This code is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* version 2 for more details (a copy is included in the LICENSE file that | ||
* accompanied this code). | ||
* | ||
* You should have received a copy of the GNU General Public License version | ||
* 2 along with this work; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
* or visit www.oracle.com if you need additional information or have any | ||
* questions. | ||
*/ | ||
|
||
/* @test | ||
@bug 8262731 | ||
@key headful printer | ||
@summary Verify that "PrinterJob.print" throws the expected exception, | ||
if "Printable.print" throws an exception. | ||
@run main/manual ExceptionFromPrintableIsIgnoredTest MAIN PE | ||
@run main/manual ExceptionFromPrintableIsIgnoredTest MAIN RE | ||
@run main/manual ExceptionFromPrintableIsIgnoredTest EDT PE | ||
@run main/manual ExceptionFromPrintableIsIgnoredTest EDT RE | ||
*/ | ||
|
||
import java.awt.Graphics; | ||
import java.awt.print.PageFormat; | ||
import java.awt.print.Printable; | ||
import java.awt.print.PrinterException; | ||
import java.awt.print.PrinterJob; | ||
import java.lang.reflect.InvocationTargetException; | ||
import javax.swing.SwingUtilities; | ||
|
||
public class ExceptionFromPrintableIsIgnoredTest { | ||
private enum TestThreadType {MAIN, EDT} | ||
private enum TestExceptionType {PE, RE} | ||
|
||
public static void main(String[] args) { | ||
if (args.length < 2) { | ||
throw new RuntimeException("Two arguments are expected:" | ||
+ " test thread type and test exception type."); | ||
} | ||
|
||
new ExceptionFromPrintableIsIgnoredTest( | ||
TestThreadType.valueOf(args[0]), | ||
TestExceptionType.valueOf(args[1])); | ||
} | ||
|
||
public ExceptionFromPrintableIsIgnoredTest( | ||
final TestThreadType threadType, | ||
final TestExceptionType exceptionType) { | ||
System.out.println(String.format( | ||
"Test started. threadType='%s', exceptionType='%s'", | ||
threadType, exceptionType)); | ||
|
||
if (threadType == TestThreadType.MAIN) { | ||
runTest(exceptionType); | ||
} else if (threadType == TestThreadType.EDT) { | ||
try { | ||
SwingUtilities.invokeAndWait(new Runnable() { | ||
@Override | ||
public void run() { | ||
runTest(exceptionType); | ||
} | ||
}); | ||
} catch (InterruptedException | InvocationTargetException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
System.out.println("Test passed."); | ||
} | ||
|
||
private void runTest(final TestExceptionType exceptionType) { | ||
PrinterJob job = PrinterJob.getPrinterJob(); | ||
if (job.getPrintService() == null) { | ||
throw new RuntimeException("No printers are available."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where does this exception go if it happens and you are on EDT ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this exception happens on EDT, this exception as any other exception thrown from the method "runTest" will be perfectly caught and handled in "catch" block of the constructor of the test "ExceptionFromPrintableIsIgnoredTest" as "InvocationTargetException" instance, which will have this "RuntimeException" as its cause. The whole test relies on this approach of throwing and catching the exception and it works. |
||
} | ||
|
||
job.setPrintable(new Printable() { | ||
@Override | ||
public int print(Graphics graphics, PageFormat pageFormat, | ||
int pageIndex) throws PrinterException { | ||
if (pageIndex > 1) { | ||
return NO_SUCH_PAGE; | ||
} | ||
if (exceptionType == TestExceptionType.PE) { | ||
throw new PrinterException( | ||
"Exception from 'Printable.print'."); | ||
} else if (exceptionType == TestExceptionType.RE) { | ||
throw new RuntimeException( | ||
"Exception from 'Printable.print'."); | ||
} | ||
return PAGE_EXISTS; | ||
} | ||
}); | ||
|
||
Throwable printEx = null; | ||
try { | ||
job.print(); | ||
} catch (Throwable t) { | ||
printEx = t; | ||
} | ||
|
||
if (printEx != null) { | ||
System.out.println("'PrinterJob.print' threw the exception:"); | ||
printEx.printStackTrace(System.out); | ||
|
||
if (((printEx instanceof PrinterException) && | ||
(exceptionType == TestExceptionType.PE)) || | ||
((printEx instanceof RuntimeException) && | ||
(exceptionType == TestExceptionType.RE))) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Line 115 printEx.printStackTrace(System.out); This line in the test is done exactly as the evidence that the exception was really propagated from the method "PrinterJob.print()" and caught by the test. I verified that it is "true" on macOS, Windows, Linux. I reported this in the code review and in JBS bug record itself that in JDK 17 and in JDK 8u291 and even in JDK 8-b132 on Windows and on Linux "PrinterJob.print()", which is called on EDT, propagates "PrinterException" and "RuntimeException" which are thrown from "Printable.print(Graphics, PageFormat, int)" method. If you still do not trust this, then I suggest you just to recheck it yourself by any trustworthy mean in addition to this regression test and the purely manual test case "ExceptionFromPrintableIsIgnored.java" attached to the bug record.
From my viewpoint not wrapping "RuntimeException" into "PrinterException" and propagating "RuntimeException" through "PrinterJob.print()" will just bring behavior of JDK on macOS into correspondence with existing behavior of JDK on Windows and Linux. If we wrap "RuntimeException" into "PrinterException" on macOS, then at some point in time somebody will need to change JDK code for Windows and Linux to also do the same wrapping of the exception which is currently not done. I do not insist on any variant. |
||
return;// Test passed. | ||
} | ||
throw new RuntimeException("Unexpected exception was thrown."); | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When running on the EDT do you really want to do this ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I do really want to do this, when running on EDT, the whole test relies on this approach. When the test is run on EDT this is done via "SwingUtilities.invokeAndWait" method in the constructor of the test "ExceptionFromPrintableIsIgnoredTest" and in that constructor there is "try/catch" block. If some exception occurs on EDT and is not caught on EDT, then it will be wrapped by JDK in "InvocationTargetException" instance which will be thrown from "SwingUtilities.invokeAndWait" method and will be caught by that "try/catch" block in the constructor of the test. I checked that this code works before sending the code for review and today additionally. |
||
throw new RuntimeException( | ||
"'PrinterJob.print' did not throw any exception."); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wjy is this still manual ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test is manual, because the test initiates printing and there is a chance that on a test host a default printer will be some virtual printer which can show the native dialog asking to specify the location of PDF file in which the printed document should be saved, in this case the test will be blocked and will be killed by "jtreg" by a timeout, what is unacceptable for the automatic test which should not slow down the speed of execution of all other automatic tests. For example such a virtual printer on Windows OS can be "Microsoft Print to PDF". By the way, I already explained this reason in one of my replies to your questions in the previous iteration of the code review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought I also explained that the printer keyword means it would only be run if the system has a real printer by virtue of it being set in the jtreg run. You could also do that on a virtual printer if you didn't mind entering the file name