diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp index f6fd08bdcc7..b8954a97df7 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp @@ -340,7 +340,7 @@ void G1GCPhaseTimes::trace_phase(WorkerDataArray* phase, bool print_sum, } } -#define TIME_FORMAT "%.1lfms" +#define TIME_FORMAT "%.2lfms" void G1GCPhaseTimes::info_time(const char* name, double value) const { log_info(gc, phases)(" %s: " TIME_FORMAT, name, value); diff --git a/src/hotspot/share/gc/shared/weakProcessorTimes.cpp b/src/hotspot/share/gc/shared/weakProcessorTimes.cpp index ee9db87f2e6..ec979e32899 100644 --- a/src/hotspot/share/gc/shared/weakProcessorTimes.cpp +++ b/src/hotspot/share/gc/shared/weakProcessorTimes.cpp @@ -173,7 +173,7 @@ static const char* indent_str(size_t i) { return indents[MIN2(i, max_indents_index)]; } -#define TIME_FORMAT "%.1lfms" +#define TIME_FORMAT "%.2lfms" void WeakProcessorTimes::log_summary(OopStorageSet::WeakId id, uint indent) const { LogTarget(Debug, gc, phases) lt; diff --git a/src/hotspot/share/gc/shared/workerDataArray.cpp b/src/hotspot/share/gc/shared/workerDataArray.cpp index b7140662210..b2966837902 100644 --- a/src/hotspot/share/gc/shared/workerDataArray.cpp +++ b/src/hotspot/share/gc/shared/workerDataArray.cpp @@ -36,6 +36,8 @@ double WorkerDataArray::uninitialized() { return -1.0; } +#define WDA_TIME_FORMAT "%4.2lf" + template <> void WorkerDataArray::WDAPrinter::summary(outputStream* out, double time) { out->print_cr(" %.1lfms", time * MILLIUNITS); @@ -48,9 +50,10 @@ void WorkerDataArray::WDAPrinter::summary(outputStream* out, size_t valu template <> void WorkerDataArray::WDAPrinter::summary(outputStream* out, double min, double avg, double max, double diff, double sum, bool print_sum) { - out->print(" Min: %4.1lf, Avg: %4.1lf, Max: %4.1lf, Diff: %4.1lf", min * MILLIUNITS, avg * MILLIUNITS, max * MILLIUNITS, diff* MILLIUNITS); + out->print(" Min: " WDA_TIME_FORMAT ", Avg: " WDA_TIME_FORMAT ", Max: " WDA_TIME_FORMAT ", Diff: " WDA_TIME_FORMAT, + min * MILLIUNITS, avg * MILLIUNITS, max * MILLIUNITS, diff* MILLIUNITS); if (print_sum) { - out->print(", Sum: %4.1lf", sum * MILLIUNITS); + out->print(", Sum: " WDA_TIME_FORMAT, sum * MILLIUNITS); } } @@ -68,7 +71,7 @@ void WorkerDataArray::WDAPrinter::details(const WorkerDataArray* for (uint i = 0; i < phase->_length; ++i) { double value = phase->get(i); if (value != phase->uninitialized()) { - out->print(" %4.1lf", phase->get(i) * 1000.0); + out->print(" " WDA_TIME_FORMAT, phase->get(i) * 1000.0); } else { out->print(" -"); } diff --git a/src/java.base/share/classes/java/util/regex/Pattern.java b/src/java.base/share/classes/java/util/regex/Pattern.java index 49233dda6e4..98ce8a5812c 100644 --- a/src/java.base/share/classes/java/util/regex/Pattern.java +++ b/src/java.base/share/classes/java/util/regex/Pattern.java @@ -5364,10 +5364,10 @@ boolean match(Matcher matcher, int i, CharSequence seq) { * they are ignored for purposes of finding word boundaries. */ static final class Bound extends Node { - static int LEFT = 0x1; - static int RIGHT= 0x2; - static int BOTH = 0x3; - static int NONE = 0x4; + static final int LEFT = 0x1; + static final int RIGHT= 0x2; + static final int BOTH = 0x3; + static final int NONE = 0x4; int type; boolean useUWORD; Bound(int n, boolean useUWORD) { diff --git a/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java b/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java index c8a6a476e66..675b48b6399 100644 --- a/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java +++ b/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java @@ -332,7 +332,6 @@ int implRead(char[] cbuf, int off, int end) throws IOException { eof = true; if ((cb.position() == 0) && (!bb.hasRemaining())) break; - decoder.reset(); } continue; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java index 82da2f9e8c1..25ef9a99fbe 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java @@ -882,8 +882,15 @@ public void paintIcon(Component c, Graphics g, int x, int y) { } } if (icon != null) { - icon.paintIcon(c, g, x + VistaMenuItemCheckIconFactory.getIconWidth(), - y + OFFSET); + if (WindowsGraphicsUtils.isLeftToRight(c)) { + icon.paintIcon(c, g, + x + VistaMenuItemCheckIconFactory.getIconWidth(), + y + OFFSET); + } else { + icon.paintIcon(c, g, + x - VistaMenuItemCheckIconFactory.getIconWidth() + 2 * OFFSET, + y + OFFSET); + } } } private static WindowsMenuItemUIAccessor getAccessor( diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index 7aa52eadfb8..0fa284294e2 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -43,6 +43,7 @@ import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuItem; +import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; @@ -215,8 +216,17 @@ static void paintMenuItem(WindowsMenuItemUIAccessor accessor, Graphics g, if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) { Rectangle rect = lr.getTextRect(); - - rect.x += lh.getAfterCheckIconGap(); + if (menuItem.getComponentOrientation().isLeftToRight()) { + if (menuItem.getHorizontalTextPosition() != SwingConstants.LEADING + && menuItem.getHorizontalTextPosition() != SwingConstants.LEFT) { + rect.x += lh.getAfterCheckIconGap(); + } + } else { + if (menuItem.getHorizontalTextPosition() != SwingConstants.LEADING + && menuItem.getHorizontalTextPosition() != SwingConstants.RIGHT) { + rect.x -= lh.getAfterCheckIconGap(); + } + } lr.setTextRect(rect); } @@ -232,7 +242,11 @@ static void paintMenuItem(WindowsMenuItemUIAccessor accessor, Graphics g, } if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) { Rectangle rect = lr.getAccRect(); - rect.x += lh.getAfterCheckIconGap(); + if (menuItem.getComponentOrientation().isLeftToRight()) { + rect.x += lh.getAfterCheckIconGap(); + } else { + rect.x -= lh.getAfterCheckIconGap(); + } lr.setAccRect(rect); } SwingUtilities3.paintAccText(g, lh, lr, disabledForeground, diff --git a/src/jdk.jpackage/windows/native/applauncher/WinLauncher.cpp b/src/jdk.jpackage/windows/native/applauncher/WinLauncher.cpp index 2a8aaf8ad8d..ef001a9c344 100644 --- a/src/jdk.jpackage/windows/native/applauncher/WinLauncher.cpp +++ b/src/jdk.jpackage/windows/native/applauncher/WinLauncher.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -210,6 +210,16 @@ class RunExecutorWithMsgLoop { }; +void enableConsoleCtrlHandler(bool enable) { + if (!SetConsoleCtrlHandler(NULL, enable ? FALSE : TRUE)) { + JP_THROW(SysError(tstrings::any() << "SetConsoleCtrlHandler(NULL, " + << (enable ? "FALSE" : "TRUE") + << ") failed", + SetConsoleCtrlHandler)); + } +} + + void launchApp() { // [RT-31061] otherwise UI can be left in back of other windows. ::AllowSetForegroundWindow(ASFW_ANY); @@ -256,6 +266,19 @@ void launchApp() { exec.arg(arg); }); + exec.afterProcessCreated([&](HANDLE pid) { + // + // Ignore Ctrl+C in the current process. + // This will prevent child process termination without allowing + // it to handle Ctrl+C events. + // + // Disable the default Ctrl+C handler *after* the child process + // has been created as it is inheritable and we want the child + // process to have the default handler. + // + enableConsoleCtrlHandler(false); + }); + DWORD exitCode = RunExecutorWithMsgLoop::apply(exec); exit(exitCode); diff --git a/src/jdk.jpackage/windows/native/common/Executor.cpp b/src/jdk.jpackage/windows/native/common/Executor.cpp index edb850afdbb..dfb6b299e5d 100644 --- a/src/jdk.jpackage/windows/native/common/Executor.cpp +++ b/src/jdk.jpackage/windows/native/common/Executor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -161,6 +161,10 @@ UniqueHandle Executor::startProcess(UniqueHandle* threadHandle) const { } } + if (afterProcessCreatedCallback) { + afterProcessCreatedCallback(processInfo.hProcess); + } + // Return process handle. return UniqueHandle(processInfo.hProcess); } diff --git a/src/jdk.jpackage/windows/native/common/Executor.h b/src/jdk.jpackage/windows/native/common/Executor.h index a6edcbd4f76..09c9f85bac6 100644 --- a/src/jdk.jpackage/windows/native/common/Executor.h +++ b/src/jdk.jpackage/windows/native/common/Executor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -26,6 +26,8 @@ #ifndef EXECUTOR_H #define EXECUTOR_H +#include + #include "tstrings.h" #include "UniqueHandle.h" @@ -97,6 +99,14 @@ class Executor { */ int execAndWaitForExit() const; + /** + * Call provided function after the process hass been created. + */ + Executor& afterProcessCreated(const std::function& v) { + afterProcessCreatedCallback = v; + return *this; + } + private: UniqueHandle startProcess(UniqueHandle* threadHandle=0) const; @@ -106,6 +116,7 @@ class Executor { HANDLE jobHandle; tstring_array argsArray; std::wstring appPath; + std::function afterProcessCreatedCallback; }; #endif // #ifndef EXECUTOR_H diff --git a/test/hotspot/gtest/gc/shared/test_workerDataArray.cpp b/test/hotspot/gtest/gc/shared/test_workerDataArray.cpp index 0c52dae6b7d..6f612b56da7 100644 --- a/test/hotspot/gtest/gc/shared/test_workerDataArray.cpp +++ b/test/hotspot/gtest/gc/shared/test_workerDataArray.cpp @@ -102,9 +102,9 @@ const char* WorkerDataArrayTest::format_summary( double min, double avg, double max, double diff, double sum, size_t workers) { stringStream out; - out.print(" Min: %4.1lf" - ", Avg: %4.1lf, Max: %4.1lf" - ", Diff: %4.1lf, Sum: %4.1lf" + out.print(" Min: %4.2lf" + ", Avg: %4.2lf, Max: %4.2lf" + ", Diff: %4.2lf, Sum: %4.2lf" ", Workers: " SIZE_FORMAT "\n", min, avg, max, diff, sum, workers); return out.as_string(); @@ -278,12 +278,12 @@ class UninitializedDoubleElementWorkerDataArrayTest : public WorkerDataArrayTest private: virtual const char* expected_summary() { - return format_summary(5.1, 6.1, 7.2, 2.1, 12.3, 2); + return format_summary(5.10, 6.15, 7.20, 2.10, 12.30, 2); } virtual const char* expected_details() { stringStream out; - out.print(" %4.1lf - %4.1lf\n", 5.1, 7.2); + out.print(" %4.2lf - %4.2lf\n", 5.1, 7.2); return out.as_string(); } }; diff --git a/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java b/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java new file mode 100644 index 00000000000..95759df5522 --- /dev/null +++ b/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022, 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 8292043 + * @run testng StatefulDecoderNearEOF + * @summary Check MalformedInputException is thrown with stateful decoders + * with malformed input before EOF + */ + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.MalformedInputException; +import java.nio.charset.StandardCharsets; +import java.util.stream.IntStream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertThrows; + +@Test +public class StatefulDecoderNearEOF { + + @DataProvider + public Object[][] inputs() { + return new Object[][] { + // BOM, followed by High surrogate (in UTF-16LE). + // First read() should throw an exception. + {new byte[] {(byte)0xff, (byte)0xfe, 0, (byte)0xd8}, 0}, + + // BOM, followed by 'A', 'B', 'C', then by High surrogate (in UTF-16LE). + // Fourth read() should throw an exception. + {new byte[] {(byte)0xff, (byte)0xfe, (byte)0x41, 0, (byte)0x42, 0, (byte)0x43, 0, 0, (byte)0xd8}, 3}, + }; + } + + @Test (dataProvider = "inputs") + public void testStatefulDecoderNearEOF(byte[] ba, int numSucessReads) throws IOException { + try (var r = new InputStreamReader( + new ByteArrayInputStream(ba), + StandardCharsets.UTF_16.newDecoder().onMalformedInput(CodingErrorAction.REPORT))) { + // Issue read() as many as numSucessReads which should not fail + IntStream.rangeClosed(1, numSucessReads).forEach(i -> { + try { + assertEquals(r.read(), (int)ba[i * 2]); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + + // Final dangling high surrogate should throw an exception + assertThrows(MalformedInputException.class, () -> r.read()); + } + } +} diff --git a/test/jdk/javax/swing/JComponent/bug4235215.java b/test/jdk/javax/swing/JComponent/bug4235215.java new file mode 100644 index 00000000000..471f713ee46 --- /dev/null +++ b/test/jdk/javax/swing/JComponent/bug4235215.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1999, 2025, 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 4235215 + * @summary Tests that Toolkit.getPrintJob() do not throw NPE + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4235215 + */ + +import java.awt.Toolkit; +import javax.swing.JButton; +import javax.swing.JFrame; + +public class bug4235215 { + + private static final String INSTRUCTIONS = """ + Press "Print Dialog" button. + If you see a print dialog, test passes. + Click "Cancel" button to close it."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4235215 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug4235215::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4235215"); + JButton button = new JButton("Print Dialog"); + button.addActionListener(ev -> { + Toolkit.getDefaultToolkit().getPrintJob(frame, "Test Printing", null); + }); + frame.add(button); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JComponent/bug4247610.java b/test/jdk/javax/swing/JComponent/bug4247610.java new file mode 100644 index 00000000000..e5470606f6e --- /dev/null +++ b/test/jdk/javax/swing/JComponent/bug4247610.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2000, 2025, 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 4247610 + * @summary Tests an unnecessary repaint issue + * @key headful + * @run main bug4247610 + */ + +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import javax.swing.JButton; +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Random; + +public class bug4247610 { + + private static JFrame frame; + private static JButton damager; + private static volatile Point loc; + private static volatile Dimension size; + private static volatile boolean traced; + private static volatile boolean failed; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("bug4247610"); + JDesktopPane pane = new JDesktopPane(); + + JInternalFrame jif = new JInternalFrame( + "Damager", true, true, true, true); + InternalFramePanel ifp = new InternalFramePanel(); + damager = new JButton("Damage!"); + ifp.add(damager); + jif.setContentPane(ifp); + jif.setBounds(0, 0, 300, 300); + jif.setVisible(true); + pane.add(jif); + + jif = new JInternalFrame("Damagee", true, true, true, true); + JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + final JLabel damagee = new JLabel(""); + panel.add(damagee); + jif.setContentPane(panel); + jif.setBounds(60, 220, 300, 100); + jif.setVisible(true); + pane.add(jif); + + final Random random = new Random(); + + damager.addActionListener((e) -> { + System.out.println("trace paints enabled"); + traced = true; + damagee.setText(Integer.toString(random.nextInt())); + }); + frame.setContentPane(pane); + frame.setSize(500, 500); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + loc = damager.getLocationOnScreen(); + size = damager.getSize(); + }); + robot.mouseMove(loc.x + size.width / 2, loc.y + size.height / 2); + robot.waitForIdle(); + robot.delay(200); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + if (failed) { + throw new RuntimeException("Failed: unnecessary repaint occured"); + } + } + + + static class InternalFramePanel extends JPanel { + final AtomicInteger repaintCounter = new AtomicInteger(0); + InternalFramePanel() { + super(new FlowLayout()); + setOpaque(true); + } + + public synchronized void paintComponent(Graphics g) { + super.paintComponent(g); + repaintCounter.incrementAndGet(); + System.out.println("repaintCounter " + repaintCounter.intValue()); + if (traced) { + failed = true; + } + } + } +} diff --git a/test/jdk/javax/swing/JComponent/bug4254995.java b/test/jdk/javax/swing/JComponent/bug4254995.java new file mode 100644 index 00000000000..8947558f128 --- /dev/null +++ b/test/jdk/javax/swing/JComponent/bug4254995.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1999, 2025, 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 4254995 + * @summary Tests that html in renderer works correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4254995 + */ + +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JScrollPane; + +public class bug4254995 { + + private static final String INSTRUCTIONS = """ + If you see a list containing digits from one to seven, test passes. + Otherwise it fails."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4254995 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug4254995::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4254995"); + String[] data = { "1", "2", "3", "4", "5", "6", "7" }; + frame.add(new JScrollPane(new JList(data))); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JMenuItem/RightLeftOrientation.java b/test/jdk/javax/swing/JMenuItem/RightLeftOrientation.java index 7080f03ad2a..247d8b52541 100644 --- a/test/jdk/javax/swing/JMenuItem/RightLeftOrientation.java +++ b/test/jdk/javax/swing/JMenuItem/RightLeftOrientation.java @@ -45,7 +45,7 @@ /* * @test id=windows - * @bug 4211052 + * @bug 4211052 8370465 * @requires (os.family == "windows") * @summary Verifies if menu items lay out correctly when their * ComponentOrientation property is set to RIGHT_TO_LEFT. @@ -155,6 +155,16 @@ static void addMenuItems(JMenu menu, ComponentOrientation o) { menuItem.setHorizontalTextPosition(SwingConstants.LEADING); menu.add(menuItem); + menuItem = new JMenuItem("Text to the left", new MyMenuItemIcon()); + menuItem.setComponentOrientation(o); + menuItem.setHorizontalTextPosition(SwingConstants.LEFT); + menu.add(menuItem); + + menuItem = new JMenuItem("Text to the right", new MyMenuItemIcon()); + menuItem.setComponentOrientation(o); + menuItem.setHorizontalTextPosition(SwingConstants.RIGHT); + menu.add(menuItem); + menuItem = new JRadioButtonMenuItem("Radio Button Menu Item"); menuItem.setComponentOrientation(o); menuItem.setSelected(true); diff --git a/test/jdk/tools/jpackage/apps/UseShutdownHook.java b/test/jdk/tools/jpackage/apps/UseShutdownHook.java new file mode 100644 index 00000000000..c558ee85701 --- /dev/null +++ b/test/jdk/tools/jpackage/apps/UseShutdownHook.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025, 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. + */ + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +public class UseShutdownHook { + + public static void main(String[] args) throws InterruptedException { + trace("Started"); + + var outputFile = Path.of(args[0]); + trace(String.format("Write output in [%s] file", outputFile)); + + var shutdownTimeoutSeconds = Integer.parseInt(args[1]); + trace(String.format("Automatically shutdown the app in %ss", shutdownTimeoutSeconds)); + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + output(outputFile, "shutdown hook executed"); + } + }); + + var startTime = System.currentTimeMillis(); + var lock = new Object(); + do { + synchronized (lock) { + lock.wait(shutdownTimeoutSeconds * 1000); + } + } while ((System.currentTimeMillis() - startTime) < (shutdownTimeoutSeconds * 1000)); + + output(outputFile, "exit"); + } + + private static void output(Path outputFilePath, String msg) { + + trace(String.format("Writing [%s] into [%s]", msg, outputFilePath)); + + try { + Files.createDirectories(outputFilePath.getParent()); + Files.writeString(outputFilePath, msg, StandardOpenOption.APPEND, StandardOpenOption.CREATE); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static void trace(String msg) { + Date time = new Date(System.currentTimeMillis()); + msg = String.format("UseShutdownHook [%s]: %s", SDF.format(time), msg); + System.out.println(msg); + try { + Files.write(traceFile, List.of(msg), StandardOpenOption.APPEND, StandardOpenOption.CREATE); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static final SimpleDateFormat SDF = new SimpleDateFormat("HH:mm:ss.SSS"); + + private static final Path traceFile = Path.of(System.getProperty("jpackage.test.trace-file")); +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CfgFile.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CfgFile.java index 2604e266d5a..9e3a894ec78 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CfgFile.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CfgFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -59,13 +59,18 @@ public String getValueUnchecked(String sectionName, String key) { } } - public void addValue(String sectionName, String key, String value) { + public CfgFile addValue(String sectionName, String key, String value) { var section = getSection(sectionName); if (section == null) { section = new Section(sectionName, new ArrayList<>()); data.add(section); } section.data.add(Map.entry(key, value)); + return this; + } + + public CfgFile add(CfgFile other) { + return combine(this, other); } public CfgFile() { @@ -89,7 +94,7 @@ private CfgFile(List
data, String id) { this.id = id; } - public void save(Path path) { + public CfgFile save(Path path) { var lines = data.stream().flatMap(section -> { return Stream.concat( Stream.of(String.format("[%s]", section.name)), @@ -98,6 +103,7 @@ public void save(Path path) { })); }); TKit.createTextFile(path, lines); + return this; } private Section getSection(String name) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java index 76639d30128..116712afd73 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -270,7 +271,7 @@ PackageTest addHelloAppFileAssociationsVerifier(FileAssociations fa, TKit.trace(String.format("Use desktop to open [%s] file", testFile)); Desktop.getDesktop().open(testFile.toFile()); - TKit.waitForFileCreated(appOutput, 7); + TKit.waitForFileCreated(appOutput, Duration.ofSeconds(7), Duration.ofSeconds(3)); List expectedArgs = new ArrayList<>(List.of( faLauncherDefaultArgs)); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java index e9f06e3512a..419f0ca129f 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java @@ -35,8 +35,9 @@ import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; -import java.nio.file.WatchService; import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -242,6 +243,13 @@ public static void createPropertiesFile(Path propsFilename, trace("Done"); } + public static void traceFileContents(Path path, String label) throws IOException { + assertFileExists(path); + trace(String.format("Dump [%s] %s...", path, label)); + Files.readAllLines(path).forEach(TKit::trace); + trace("Done"); + } + public static void createPropertiesFile(Path propsFilename, Map.Entry... props) { createPropertiesFile(propsFilename, List.of(props)); @@ -521,49 +529,57 @@ public static Path createRelativePathCopy(final Path file) { return file; } - static void waitForFileCreated(Path fileToWaitFor, - long timeoutSeconds) throws IOException { + public static void waitForFileCreated(Path fileToWaitFor, + Duration timeout, Duration afterCreatedTimeout) throws IOException { + waitForFileCreated(fileToWaitFor, timeout); + // Wait after the file has been created to ensure it is fully written. + ThrowingConsumer.toConsumer(Thread::sleep).accept(afterCreatedTimeout.getSeconds()); + } + + private static void waitForFileCreated(Path fileToWaitFor, Duration timeout) throws IOException { trace(String.format("Wait for file [%s] to be available", fileToWaitFor.toAbsolutePath())); - WatchService ws = FileSystems.getDefault().newWatchService(); - - Path watchDirectory = fileToWaitFor.toAbsolutePath().getParent(); - watchDirectory.register(ws, ENTRY_CREATE, ENTRY_MODIFY); - - long waitUntil = System.currentTimeMillis() + timeoutSeconds * 1000; - for (;;) { - long timeout = waitUntil - System.currentTimeMillis(); - assertTrue(timeout > 0, String.format( - "Check timeout value %d is positive", timeout)); - - WatchKey key = ThrowingSupplier.toSupplier(() -> ws.poll(timeout, - TimeUnit.MILLISECONDS)).get(); - if (key == null) { - if (fileToWaitFor.toFile().exists()) { - trace(String.format( - "File [%s] is available after poll timeout expired", - fileToWaitFor)); - return; + try (var ws = FileSystems.getDefault().newWatchService()) { + + Path watchDirectory = fileToWaitFor.toAbsolutePath().getParent(); + watchDirectory.register(ws, ENTRY_CREATE, ENTRY_MODIFY); + + var waitUntil = Instant.now().plus(timeout); + for (;;) { + Instant n = Instant.now(); + Duration remainderTimeout = Duration.between(n, waitUntil); + assertTrue(!remainderTimeout.isNegative() && !remainderTimeout.isZero(), String.format( + "Check timeout value %dms is positive", remainderTimeout.toMillis())); + + WatchKey key = ThrowingSupplier.toSupplier(() -> { + return ws.poll(remainderTimeout.toMillis(), TimeUnit.MILLISECONDS); + }).get(); + if (key == null) { + if (Files.exists(fileToWaitFor)) { + trace(String.format( + "File [%s] is available after poll timeout expired", + fileToWaitFor)); + return; + } + assertUnexpected(String.format("Timeout %dms expired", remainderTimeout.toMillis())); } - assertUnexpected(String.format("Timeout expired", timeout)); - } - for (WatchEvent event : key.pollEvents()) { - if (event.kind() == StandardWatchEventKinds.OVERFLOW) { - continue; - } - Path contextPath = (Path) event.context(); - if (Files.isSameFile(watchDirectory.resolve(contextPath), - fileToWaitFor)) { - trace(String.format("File [%s] is available", fileToWaitFor)); - return; + for (WatchEvent event : key.pollEvents()) { + if (event.kind() == StandardWatchEventKinds.OVERFLOW) { + continue; + } + Path contextPath = (Path) event.context(); + if (Files.exists(fileToWaitFor) && Files.isSameFile(watchDirectory.resolve(contextPath), fileToWaitFor)) { + trace(String.format("File [%s] is available", fileToWaitFor)); + return; + } } - } - if (!key.reset()) { - assertUnexpected("Watch key invalidated"); + if (!key.reset()) { + assertUnexpected("Watch key invalidated"); + } } } } diff --git a/test/jdk/tools/jpackage/resources/Win8365790Test.ps1 b/test/jdk/tools/jpackage/resources/Win8365790Test.ps1 new file mode 100644 index 00000000000..3a7d8c9a90b --- /dev/null +++ b/test/jdk/tools/jpackage/resources/Win8365790Test.ps1 @@ -0,0 +1,83 @@ +# +# Copyright (c) 2025, 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. +# + +param ( + # Path to executable to start. + [Parameter(Mandatory=$true)] + [string]$Executable, + + # Timeout to wait after the executable has been started. + [Parameter(Mandatory=$true)] + [double]$TimeoutSeconds +) + +$type = @{ + TypeDefinition = @' +using System; +using System.Runtime.InteropServices; + +namespace Stuff { + + internal struct Details { + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId); + } + + public struct Facade { + public static void GenerateConsoleCtrlEvent() { + if (!Details.GenerateConsoleCtrlEvent(0, 0)) { + reportLastErrorAndExit("GenerateConsoleCtrlEvent"); + } + } + + internal static void reportLastErrorAndExit(String func) { + int errorCode = Marshal.GetLastWin32Error(); + Console.Error.WriteLine(func + " function failed with error code: " + errorCode); + Environment.Exit(100); + } + } +} +'@ +} +Add-Type @type + +Set-PSDebug -Trace 2 + +# Launch the target executable. +# `-NoNewWindow` parameter will attach the started process to the existing console. +$childProc = Start-Process -PassThru -NoNewWindow $Executable + +# Wait a bit to let the started process complete initialization. +Start-Sleep -Seconds $TimeoutSeconds + +# Call GenerateConsoleCtrlEvent to send a CTRL+C event to the launched executable. +# CTRL+C event will be sent to all processes attached to the console of the current process, +# i.e., it will be sent to this PowerShell process and to the started $Executable process because +# it was configured to attach to the existing console (the console of this PowerShell process). +[Stuff.Facade]::GenerateConsoleCtrlEvent() + +# Wait for child process termination +Wait-Process -InputObject $childProc + +Exit 0 diff --git a/test/jdk/tools/jpackage/windows/Win8365790Test.java b/test/jdk/tools/jpackage/windows/Win8365790Test.java new file mode 100644 index 00000000000..4b2a18b04f6 --- /dev/null +++ b/test/jdk/tools/jpackage/windows/Win8365790Test.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2025, 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. + */ + +import static jdk.jpackage.test.HelloApp.configureAndExecute; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.CfgFile; +import jdk.jpackage.test.Executor; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; + +/** + * Test the child process has a chance to handle Ctrl+C signal. + */ + +/* + * @test + * @summary Test case for JDK-8365790 + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.* + * @build Win8365790Test + * @requires (os.family == "windows") + * @run main/othervm/timeout=100 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=Win8365790Test + */ +public class Win8365790Test { + + @Test + public void test() throws InterruptedException, IOException { + + var outputDir = TKit.createTempDirectory("response-dir"); + + var mainOutputFile = outputDir.resolve("output.txt"); + var mainTraceFile = outputDir.resolve("trace.txt"); + + var probeOutputFile = outputDir.resolve("probe-output.txt"); + var probeTraceFile = outputDir.resolve("probe-trace.txt"); + + var cmd = JPackageCommand + .helloAppImage(TEST_APP_JAVA + "*UseShutdownHook") + .ignoreFakeRuntime() + .addArguments("--java-options", "-Djpackage.test.trace-file=" + mainTraceFile.toString()) + .addArguments("--arguments", mainOutputFile.toString()) + .addArguments("--arguments", Long.toString(Duration.ofSeconds(TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS).getSeconds())); + + new AdditionalLauncher("probe") { + @Override + protected void verify(JPackageCommand cmd) { + } + }.addJavaOptions("-Djpackage.test.trace-file=" + probeTraceFile.toString()) + .addDefaultArguments(probeOutputFile.toString(), Long.toString(Duration.ofSeconds(TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS).getSeconds())) + .applyTo(cmd); + + cmd.executeAndAssertImageCreated(); + + cmd.readLauncherCfgFile("probe") + .add(new CfgFile().addValue("Application", "win.norestart", Boolean.TRUE.toString())) + .save(cmd.appLauncherCfgPath("probe")); + + // Try Ctrl+C signal on a launcher with disabled restart functionality. + // It will create a single launcher process instead of the parent and the child processes. + // Ctrl+C always worked for launcher with disabled restart functionality. + var probeOutput = runLauncher(cmd, "probe", probeTraceFile, probeOutputFile); + + if (!probeOutput.equals("shutdown hook executed")) { + // Ctrl+C signal didn't make it. Test environment doesn't support Ctrl+C signal + // delivery from the prowershell process to a child process, don't run the main + // test. + TKit.throwSkippedException( + "The environment does NOT support Ctrl+C signal delivery from the prowershell process to a child process"); + } + + var mainOutput = runLauncher(cmd, null, mainTraceFile, mainOutputFile); + + TKit.assertEquals("shutdown hook executed", mainOutput, "Check shutdown hook executed"); + } + + private static String runLauncher(JPackageCommand cmd, String launcherName, Path traceFile, Path outputFile) throws IOException { + // Launch the specified launcher and send Ctrl+C signal to it. + Thread t = new Thread (() -> { + configureAndExecute(0, Executor.of("powershell", "-NonInteractive", "-NoLogo", "-NoProfile", "-ExecutionPolicy", "Unrestricted") + .addArgument("-File").addArgument(TEST_PS1) + .addArguments("-TimeoutSeconds", Long.toString(Duration.ofSeconds(5).getSeconds())) + .addArgument("-Executable").addArgument(cmd.appLauncherPath(launcherName)) + .dumpOutput()); + }); + t.start(); + + TKit.waitForFileCreated(traceFile, Duration.ofSeconds(20), Duration.ofSeconds(2)); + + try { + TKit.waitForFileCreated(outputFile, Duration.ofSeconds(TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS * 2), Duration.ofSeconds(2)); + } finally { + TKit.traceFileContents(traceFile, "Test app trace"); + } + + TKit.assertFileExists(outputFile); + return Files.readString(outputFile); + } + + private static final long TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS = 30; + + private static final Path TEST_APP_JAVA = TKit.TEST_SRC_ROOT.resolve("apps/UseShutdownHook.java"); + private static final Path TEST_PS1 = TKit.TEST_SRC_ROOT.resolve(Path.of("resources/Win8365790Test.ps1")).normalize(); +}