JBR-7134: Fix InputMethodTests on macOS

This commit is contained in:
Nikita Tsarev
2024-05-27 18:41:21 +02:00
parent c1394c1688
commit c5dbd5d1a3
18 changed files with 998 additions and 898 deletions

View File

@@ -25,8 +25,8 @@
* @test
* @summary Regression test for JBR-5254: CapsLock and Chinese IMs don't work properly
* @modules java.desktop/sun.lwawt.macosx
* @run main InputMethodTest PinyinCapsLockTest
* @requires (jdk.version.major >= 8 & os.family == "mac")
* @run main ChineseCapsLockTest
* @requires (jdk.version.major >= 17 & os.family == "mac")
*/
import java.util.ArrayList;
@@ -35,7 +35,7 @@ import java.util.List;
import static java.awt.event.KeyEvent.*;
public class PinyinCapsLockTest implements Runnable {
public class ChineseCapsLockTest extends TestFixture {
private static final List<String> expectLowercase = new ArrayList<>(Arrays.asList(
"com.apple.inputmethod.SCIM.ITABC",
"com.apple.inputmethod.SCIM.Shuangpin",
@@ -58,7 +58,7 @@ public class PinyinCapsLockTest implements Runnable {
));
@Override
public void run() {
public void test() throws Exception {
for (String layout : expectLowercase) {
testLatinTyping(layout, false);
}
@@ -69,13 +69,17 @@ public class PinyinCapsLockTest implements Runnable {
}
private void testLatinTyping(String layout, boolean expectUppercase) {
InputMethodTest.section(layout);
InputMethodTest.layout(layout);
InputMethodTest.setCapsLockState(true);
InputMethodTest.type(VK_A, 0);
InputMethodTest.type(VK_B, 0);
InputMethodTest.type(VK_C, 0);
InputMethodTest.expectText(expectUppercase ? "ABC" : "abc");
InputMethodTest.type(VK_ESCAPE, 0);
section(layout);
layout(layout);
setCapsLockState(true);
press(VK_A);
press(VK_B);
press(VK_C);
expectText(expectUppercase ? "ABC" : "abc");
press(VK_ESCAPE);
}
public static void main(String[] args) {
new ChineseCapsLockTest().run();
}
}

View File

@@ -25,55 +25,64 @@
* @test
* @summary Regression test for JBR-6704 Extra IME event fired when pressing a keystroke containing Ctrl and focus moving to a different window
* @modules java.desktop/sun.lwawt.macosx
* @run main InputMethodTest CtrlShortcutNewWindowTest
* @requires (jdk.version.major >= 8 & os.family == "mac")
* @run main CtrlShortcutNewWindowTest
* @requires (jdk.version.major >= 17 & os.family == "mac")
*/
import javax.swing.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import static java.awt.event.KeyEvent.*;
public class CtrlShortcutNewWindowTest implements Runnable {
public class CtrlShortcutNewWindowTest extends TestFixture {
@Override
public void run() {
var frame = new JFrame();
frame.setSize(300, 300);
frame.setLocation(100, 100);
JTextField textField = new JTextField();
frame.add(textField);
textField.requestFocusInWindow();
public void test() throws Exception {
final JFrame[] frame = {null};
final JTextField[] textField = {null};
final boolean[] keyPressed = {false};
InputMethodTest.textArea.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == VK_BACK_QUOTE && (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) {
keyPressed[0] = true;
frame.setVisible(true);
SwingUtilities.invokeAndWait(() -> {
frame[0] = new JFrame("frame 2");
frame[0].setSize(300, 300);
frame[0].setLocation(100, 100);
textField[0] = new JTextField();
frame[0].add(textField[0]);
textField[0].requestFocusInWindow();
inputArea.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == VK_BACK_QUOTE && (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) {
keyPressed[0] = true;
frame[0].setVisible(true);
}
}
}
});
});
InputMethodTest.section("Ctrl+Backtick");
InputMethodTest.layout("com.apple.keylayout.ABC");
InputMethodTest.type(VK_BACK_QUOTE, CTRL_DOWN_MASK);
InputMethodTest.delay(500);
section("Ctrl+Backtick");
layout("com.apple.keylayout.ABC");
press(VK_BACK_QUOTE, CTRL_DOWN_MASK);
delay(500);
if (!keyPressed[0]) {
InputMethodTest.fail("Ctrl+Backtick key combination not detected");
}
expect(keyPressed[0], "Ctrl+Backtick pressed");
if (!textField.getText().isEmpty()) {
InputMethodTest.fail("Extra characters in the text field");
}
String[] text = {null};
SwingUtilities.invokeAndWait(() -> {
text[0] = textField[0].getText();
});
frame.setVisible(false);
frame.dispose();
expect(text[0].isEmpty(), "Second text field empty");
SwingUtilities.invokeAndWait(() -> {
frame[0].setVisible(false);
frame[0].dispose();
});
}
public static void main(String[] args) {
new CtrlShortcutNewWindowTest().run();
}
}

View File

@@ -25,46 +25,51 @@
* @test
* @summary Regression test for JBR-5006 Dead keys exhibit invalid behavior on macOS
* @modules java.desktop/sun.lwawt.macosx
* @run main InputMethodTest DeadKeysTest
* @requires (jdk.version.major >= 8 & os.family == "mac")
* @run main DeadKeysTest
* @requires (jdk.version.major >= 17 & os.family == "mac")
*/
import static java.awt.event.KeyEvent.*;
public class DeadKeysTest implements Runnable {
public class DeadKeysTest extends TestFixture {
static private final int VK_SECTION = 0x01000000+0x00A7;
@Override
public void run() {
InputMethodTest.layout("com.apple.keylayout.ABC");
public void test() throws Exception {
layout("com.apple.keylayout.ABC");
InputMethodTest.section("ABC: Acute accent + vowel");
InputMethodTest.type(VK_E, ALT_DOWN_MASK);
InputMethodTest.type(VK_A, 0);
InputMethodTest.expectText("\u00e1");
section("ABC: Acute accent + vowel");
press(VK_E, ALT_DOWN_MASK);
press(VK_A);
expectText("\u00e1");
InputMethodTest.section("ABC: Acute accent + consonant");
InputMethodTest.type(VK_E, ALT_DOWN_MASK);
InputMethodTest.type(VK_S, 0);
InputMethodTest.expectText("\u00b4s");
section("ABC: Acute accent + consonant");
press(VK_E, ALT_DOWN_MASK);
press(VK_S);
expectText("\u00b4s");
InputMethodTest.section("ABC: Acute accent + space");
InputMethodTest.type(VK_E, ALT_DOWN_MASK);
InputMethodTest.type(VK_SPACE, 0);
InputMethodTest.expectText("\u00b4");
section("ABC: Acute accent + space");
press(VK_E, ALT_DOWN_MASK);
press(VK_SPACE);
expectText("\u00b4");
InputMethodTest.section("German - Standard: Opt+K, Section = Dead circumflex below");
InputMethodTest.layout("com.apple.keylayout.German-DIN-2137");
InputMethodTest.type(VK_K, ALT_DOWN_MASK);
InputMethodTest.type(VK_SECTION, 0);
InputMethodTest.type(VK_D, 0);
InputMethodTest.expectText("\u1e13");
section("German - Standard: Opt+K, Section = Dead circumflex below");
layout("com.apple.keylayout.German-DIN-2137");
press(VK_K, ALT_DOWN_MASK);
press(VK_SECTION);
press(VK_D);
expectText("\u1e13");
InputMethodTest.section("UnicodeHexInput: U+0041 = A");
InputMethodTest.layout("com.apple.keylayout.UnicodeHexInput");
InputMethodTest.type(VK_0, ALT_DOWN_MASK);
InputMethodTest.type(VK_0, ALT_DOWN_MASK);
InputMethodTest.type(VK_4, ALT_DOWN_MASK);
InputMethodTest.type(VK_1, ALT_DOWN_MASK);
InputMethodTest.expectText("A");
section("UnicodeHexInput: U+0041 = A");
layout("com.apple.keylayout.UnicodeHexInput");
press(VK_0, ALT_DOWN_MASK);
press(VK_0, ALT_DOWN_MASK);
press(VK_4, ALT_DOWN_MASK);
press(VK_1, ALT_DOWN_MASK);
expectText("A");
}
public static void main(String[] args) {
new DeadKeysTest().run();
}
}

View File

@@ -25,8 +25,8 @@
* @test
* @summary Regression test for JBR-5379 Last character from Korean input gets inserted once again on click
* @modules java.desktop/sun.lwawt.macosx
* @run main InputMethodTest FocusMoveUncommitedCharactersTest
* @requires (jdk.version.major >= 8 & os.family == "mac")
* @run main FocusMoveUncommitedCharactersTest
* @requires (jdk.version.major >= 17 & os.family == "mac")
*/
import javax.swing.*;
@@ -35,30 +35,34 @@ import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.List;
import static java.awt.event.KeyEvent.*;
public class FocusMoveUncommitedCharactersTest implements Runnable {
public class FocusMoveUncommitedCharactersTest extends TestFixture {
private JTextArea textArea2;
private boolean typedEventReceived = false;
@Override
public void run() {
var textArea2 = new JTextArea();
final boolean[] typedEventReceived = {false};
protected List<JComponent> getExtraComponents() {
textArea2 = new JTextArea();
textArea2.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
typedEventReceived[0] = true;
typedEventReceived = true;
}
});
InputMethodTest.frame.getContentPane().add(textArea2, BorderLayout.SOUTH);
textArea2.setVisible(true);
return List.of(textArea2);
}
InputMethodTest.layout("com.apple.inputmethod.Korean.2SetKorean");
InputMethodTest.type(VK_A, 0);
InputMethodTest.type(VK_K, 0);
@Override
public void test() throws Exception {
layout("com.apple.inputmethod.Korean.2SetKorean");
press(VK_A);
press(VK_K);
var robot = InputMethodTest.robot;
var point = new Point(textArea2.getWidth() / 2, textArea2.getHeight() / 2);
SwingUtilities.convertPointToScreen(point, textArea2);
@@ -68,7 +72,11 @@ public class FocusMoveUncommitedCharactersTest implements Runnable {
robot.delay(1000);
InputMethodTest.expectTrue(!typedEventReceived[0], "Expected no KeyTyped events on the second text area");
InputMethodTest.expectTrue(textArea2.getText().isEmpty(), "Expected second text area to be empty");
expect(!typedEventReceived, "no KeyTyped events on the second text area");
expect(textArea2.getText().isEmpty(), "second text area is empty");
}
public static void main(String[] args) {
new FocusMoveUncommitedCharactersTest().run();
}
}

View File

@@ -1,341 +0,0 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* 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 sun.lwawt.macosx.LWCToolkit;
import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.*;
import java.util.List;
import static java.awt.event.KeyEvent.KEY_PRESSED;
import static java.awt.event.KeyEvent.KEY_RELEASED;
public class InputMethodTest {
public static JFrame frame;
public static JTextArea textArea;
public static Robot robot;
private static String currentTest = "";
private static String currentSection = "";
private static String initialLayout;
private static final Set<String> addedLayouts = new HashSet<>();
private static boolean success = true;
private static final List<KeyEvent> triggeredEvents = new ArrayList<>();
private enum TestCases {
CtrlShortcutNewWindowTest (new CtrlShortcutNewWindowTest()),
DeadKeysTest (new DeadKeysTest()),
FocusMoveUncommitedCharactersTest (new FocusMoveUncommitedCharactersTest()),
JapaneseReconvertTest(new JapaneseReconvertTest()),
KeyCodesTest (new KeyCodesTest()),
NextAppWinKeyTestDead (new NextAppWinKeyTest(true)),
NextAppWinKeyTestNormal (new NextAppWinKeyTest(false)),
PinyinCapsLockTest (new PinyinCapsLockTest()),
PinyinFullWidthPunctuationTest (new PinyinFullWidthPunctuationTest()),
PinyinHalfWidthPunctuationTest (new PinyinHalfWidthPunctuationTest()),
PinyinQuotesTest (new PinyinQuotesTest()),
RomajiYenTest (new RomajiYenTest(false)),
RomajiYenBackslashTest (new RomajiYenTest(true)),
UnderlyingLayoutQWERTYTest (new UnderlyingLayoutTest(false)),
UnderlyingLayoutQWERTZTest (new UnderlyingLayoutTest(true)),
;
private Runnable test;
TestCases(Runnable runnable) {
test = runnable;
}
public void run() {
test.run();
}
}
public static void main(String[] args) {
init();
try {
for (String arg : args) {
runTest(arg);
}
} finally {
setCapsLockState(false);
LWCToolkit.switchKeyboardLayout(initialLayout);
for (String layoutId : addedLayouts) {
try {
LWCToolkit.disableKeyboardLayout(layoutId);
} catch (Exception ignored) {}
}
}
if (success) {
System.out.println("TEST PASSED");
} else {
throw new RuntimeException("TEST FAILED: check output");
}
}
private static void init() {
try {
robot = new Robot();
robot.setAutoDelay(100);
} catch (AWTException e) {
e.printStackTrace();
System.exit(1);
}
initialLayout = LWCToolkit.getKeyboardLayoutId();
frame = new JFrame("InputMethodTest");
frame.setVisible(true);
frame.setSize(600, 300);
frame.setLocationRelativeTo(null);
textArea = new JTextArea();
textArea.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent keyEvent) {
triggeredEvents.add(keyEvent);
}
@Override
public void keyPressed(KeyEvent keyEvent) {
triggeredEvents.add(keyEvent);
}
@Override
public void keyReleased(KeyEvent keyEvent) {
triggeredEvents.add(keyEvent);
}
});
frame.setLayout(new BorderLayout());
frame.getContentPane().add(textArea, BorderLayout.NORTH);
textArea.grabFocus();
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {}
}
private static void runTest(String name) {
currentTest = name;
setCapsLockState(false);
try {
TestCases.valueOf(name).run();
} catch (Exception e) {
System.out.printf("Test %s (%s) FAILED: %s\n", currentTest, currentSection, e);
success = false;
}
}
private static String readDefault(String domain, String key) {
try {
var proc = Runtime.getRuntime().exec(new String[]{"defaults", "read", domain, key});
var exitCode = proc.waitFor();
if (exitCode == 0) {
return new Scanner(proc.getInputStream()).next();
}
} catch (Exception exc) {
exc.printStackTrace();
throw new RuntimeException("internal error");
}
return null;
}
private static void writeDefault(String domain, String key, String value) {
try {
Runtime.getRuntime().exec(new String[]{"defaults", "write", domain, key, value}).waitFor();
} catch (Exception exc) {
exc.printStackTrace();
throw new RuntimeException("internal error");
}
}
public static void section(String description) {
// clear dead key state
robot.keyPress(KeyEvent.VK_ESCAPE);
robot.keyRelease(KeyEvent.VK_ESCAPE);
currentSection = description;
textArea.setText("");
frame.setTitle(currentTest + ": " + description);
triggeredEvents.clear();
}
public static void layout(String name) {
List<String> layouts = new ArrayList<>();
if (name.matches("com\\.apple\\.inputmethod\\.(SCIM|TCIM|TYIM|Korean|VietnameseIM|Kotoeri\\.\\w+)\\.\\w+")) {
layouts.add(name.replaceFirst("\\.\\w+$", ""));
}
layouts.add(name);
for (String layout : layouts) {
if (!LWCToolkit.isKeyboardLayoutEnabled(layout)) {
LWCToolkit.enableKeyboardLayout(layout);
addedLayouts.add(layout);
}
}
LWCToolkit.switchKeyboardLayout(name);
robot.delay(250);
}
public static void setUseHalfWidthPunctuation(boolean flag) {
writeDefault("com.apple.inputmethod.CoreChineseEngineFramework", "usesHalfwidthPunctuation", flag ? "1" : "0");
}
private static void restartKotoeri() {
// Need to kill Kotoeri, since it doesn't reload the config otherwise. This makes me sad.
try {
Runtime.getRuntime().exec(new String[]{"killall", "-9", "-m", "JapaneseIM"}).waitFor();
} catch (Exception exc) {
exc.printStackTrace();
throw new RuntimeException("internal error");
}
// wait for it to restart...
robot.delay(5000);
}
public static void setUseBackslashInsteadOfYen(boolean flag) {
writeDefault("com.apple.inputmethod.Kotoeri", "JIMPrefCharacterForYenKey", flag ? "1" : "0");
restartKotoeri();
}
public static void setRomajiLayout(String layout) {
writeDefault("com.apple.inputmethod.Kotoeri", "JIMPrefRomajiKeyboardLayoutKey", layout);
restartKotoeri();
}
public static void type(int key, int modifiers) {
List<Integer> modKeys = new ArrayList<>();
if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) {
modKeys.add(KeyEvent.VK_ALT);
}
if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) {
modKeys.add(KeyEvent.VK_CONTROL);
}
if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) {
modKeys.add(KeyEvent.VK_SHIFT);
}
if ((modifiers & InputEvent.META_DOWN_MASK) != 0) {
modKeys.add(KeyEvent.VK_META);
}
for (var modKey : modKeys) {
robot.keyPress(modKey);
}
robot.keyPress(key);
robot.keyRelease(key);
for (var modKey : modKeys) {
robot.keyRelease(modKey);
}
}
public static void setCapsLockState(boolean desiredState) {
LWCToolkit.getDefaultToolkit().setLockingKeyState(KeyEvent.VK_CAPS_LOCK, desiredState);
robot.delay(250);
}
public static List<KeyEvent> getTriggeredEvents() {
return Collections.unmodifiableList(triggeredEvents);
}
public static void expectText(String expectedValue) {
var actualValue = textArea.getText();
if (actualValue.equals(expectedValue)) {
System.out.printf("Test %s (%s) passed: got '%s'\n", currentTest, currentSection, actualValue);
} else {
success = false;
System.out.printf("Test %s (%s) FAILED: expected '%s', got '%s'\n", currentTest, currentSection, expectedValue, actualValue);
}
}
public static void expectKeyPress(int vk, int location, int modifiers, boolean strict) {
var pressed = triggeredEvents.stream().filter(e -> e.getID() == KEY_PRESSED).toList();
var released = triggeredEvents.stream().filter(e -> e.getID() == KEY_RELEASED).toList();
if (pressed.size() == 1 || (pressed.size() > 1 && !strict)) {
var keyCode = pressed.get(pressed.size() - 1).getKeyCode();
expectTrue(keyCode == vk, "key press, actual key code: " + keyCode + ", expected: " + vk);
var keyLocation = pressed.get(pressed.size() - 1).getKeyLocation();
expectTrue(keyLocation == location, "key press, actual key location: " + keyLocation + ", expected: " + location);
var keyModifiers = pressed.get(pressed.size() - 1).getModifiersEx();
expectTrue(keyModifiers == modifiers, "key press, actual key modifiers: " + keyModifiers + ", expected: " + modifiers);
} else {
if (strict) {
fail("expected exactly one KEY_PRESSED event, got " + pressed.size());
} else {
fail("expected at least one KEY_PRESSED event, got none");
}
}
if (released.size() == 1 || (released.size() > 1 && !strict)) {
var keyCode = released.get(0).getKeyCode();
expectTrue(keyCode == vk, "key release, actual key code: " + keyCode + ", expected: " + vk);
var keyLocation = released.get(0).getKeyLocation();
expectTrue(keyLocation == location, "key release, actual key location: " + keyLocation + ", expected: " + location);
if (strict) {
var keyModifiers = released.get(0).getModifiersEx();
expectTrue(keyModifiers == 0, "key release, actual key modifiers: " + keyModifiers + ", expected: 0");
}
} else {
if (strict) {
fail("expected exactly one KEY_RELEASED event, got " + released.size());
} else {
fail("expected at least one KEY_RELEASED event, got none");
}
}
}
public static void expectTrue(boolean value, String comment) {
if (value) {
System.out.printf("Test %s (%s) passed: %s\n", currentTest, currentSection, comment);
} else {
success = false;
System.out.printf("Test %s (%s) FAILED: %s\n", currentTest, currentSection, comment);
}
}
public static void fail(String comment) {
expectTrue(false, comment);
}
public static void delay(int millis) {
robot.delay(millis);
}
}

View File

@@ -27,27 +27,39 @@ import static java.awt.event.KeyEvent.*;
* @test
* @summary Regression test for JBR-7119 Converting to Hanja/Kanji on macOS doesn't replace the converted Hangul/Kana symbols
* @modules java.desktop/sun.lwawt.macosx
* @run main InputMethodTest JapaneseReconvertTest
* @requires (jdk.version.major >= 8 & os.family == "mac")
* @run main JapaneseReconvertTest
* @requires (jdk.version.major >= 17 & os.family == "mac")
*/
public class JapaneseReconvertTest implements Runnable {
public class JapaneseReconvertTest extends TestFixture {
@Override
public void run() {
InputMethodTest.layout("com.apple.inputmethod.Kotoeri.RomajiTyping.Japanese");
InputMethodTest.type(VK_N, 0);
InputMethodTest.type(VK_I, 0);
InputMethodTest.type(VK_H, 0);
InputMethodTest.type(VK_O, 0);
InputMethodTest.type(VK_N, 0);
InputMethodTest.type(VK_G, 0);
InputMethodTest.type(VK_O, 0);
InputMethodTest.type(VK_ENTER, 0);
InputMethodTest.expectText("日本語");
public void test() throws Exception {
layout("com.apple.inputmethod.Kotoeri.RomajiTyping.Japanese");
press(VK_N);
press(VK_I);
press(VK_H);
press(VK_O);
press(VK_N);
press(VK_G);
press(VK_O);
press(VK_ENTER);
InputMethodTest.type(VK_R, CTRL_DOWN_MASK | SHIFT_DOWN_MASK);
InputMethodTest.type(VK_ENTER, 0);
InputMethodTest.type(VK_ENTER, 0);
InputMethodTest.expectText("日本語");
var text = getText();
press(VK_R, CTRL_DOWN_MASK | SHIFT_DOWN_MASK);
press(VK_ENTER);
press(VK_ENTER);
expectText(text);
press(VK_R, CTRL_DOWN_MASK | SHIFT_DOWN_MASK);
press(VK_UP);
press(VK_ENTER);
press(VK_ENTER);
expectNotEquals(getText(), text);
}
public static void main(String[] args) {
new JapaneseReconvertTest().run();
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2000-2023 JetBrains s.r.o.
* 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
* @summary Regression test for JBR-5558 macOS keyboard rewrite 2
* @requires (jdk.version.major >= 17 & os.family == "mac")
* @modules java.desktop/sun.lwawt.macosx
* @run main JapaneseUnderlyingLayoutTest
*/
import static java.awt.event.KeyEvent.*;
public class JapaneseUnderlyingLayoutTest extends TestFixture {
@Override
public void test() throws Exception {
testQwerty();
testQwertz();
}
private void testQwerty() {
romajiLayout("com.apple.keylayout.ABC");
qwerty("com.apple.keylayout.US");
qwerty("com.apple.keylayout.ABC");
qwerty("com.apple.keylayout.Russian");
qwerty("com.apple.inputmethod.Kotoeri.RomajiTyping.Roman");
qwerty("com.apple.inputmethod.Kotoeri.RomajiTyping.Japanese");
}
private void testQwertz() {
romajiLayout("com.apple.keylayout.ABC-QWERTZ");
qwertz("com.apple.keylayout.German");
qwertz("com.apple.keylayout.ABC-QWERTZ");
qwertz("com.apple.keylayout.Polish");
qwertz("com.apple.inputmethod.Kotoeri.RomajiTyping.Roman");
qwertz("com.apple.inputmethod.Kotoeri.RomajiTyping.Japanese");
}
private void qwerty(String layout) {
testImpl(layout, VK_Y);
}
private void qwertz(String layout) {
testImpl(layout, VK_Z);
}
private void testImpl(String layout, int vkY) {
layout(layout);
section("Cmd " + layout);
press(VK_Y, META_DOWN_MASK);
expectKeyPressed(vkY, META_DOWN_MASK);
section("Ctrl " + layout);
press(VK_Y, CTRL_DOWN_MASK);
expectKeyPressed(vkY, CTRL_DOWN_MASK);
}
public static void main(String[] args) {
new JapaneseUnderlyingLayoutTest().run();
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* 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
* @summary Regression test for JBR-5309: Minor keyboard inconsistencies on macOS
* @requires (jdk.version.major >= 17 & os.family == "mac")
* @modules java.desktop/sun.lwawt.macosx
* @run main JapaneseYenBackslashTest
*/
import static java.awt.event.KeyEvent.*;
public class JapaneseYenBackslashTest extends TestFixture {
static private final int ROBOT_KEYCODE_YEN_SYMBOL_JIS = 0x200025D;
static private final String YEN_SYMBOL = "\u00a5";
static private final String RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK = "\u00bb";
private boolean useBackslash;
@Override
public void test() throws Exception {
romajiLayout("com.apple.keylayout.ABC");
doTest(false);
doTest(true);
}
private void doTest(boolean useBackslash) {
this.useBackslash = useBackslash;
japaneseUseBackslash(useBackslash);
// We need to switch from and to the Romaji layout after the JapaneseIM is restarted.
// I'd say it's a macOS bug, but I don't think Kotoeri really expects to be killed like this.
layout("com.apple.keylayout.ABC");
layout("com.apple.inputmethod.Kotoeri.RomajiTyping.Roman");
backslash();
optBackslash();
shiftBackslash();
optShiftBackslash();
optY();
yen();
optYen();
shiftYen();
optShiftYen();
}
private void backslash() {
section("Backslash");
press(VK_BACK_SLASH);
expectText(useBackslash ? "\\" : YEN_SYMBOL);
}
private void optBackslash() {
section("Opt+Backslash");
press(VK_BACK_SLASH, ALT_DOWN_MASK);
expectText(useBackslash ? YEN_SYMBOL : "\\");
}
private void shiftBackslash() {
section("Shift+Backslash");
press(VK_BACK_SLASH, SHIFT_DOWN_MASK);
expectText("|");
}
private void optShiftBackslash() {
section("Opt+Shift+Backslash");
press(VK_BACK_SLASH, SHIFT_DOWN_MASK | ALT_DOWN_MASK);
expectText(RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK);
}
private void optY() {
section("Opt+Y");
press(VK_Y, ALT_DOWN_MASK);
expectText(useBackslash ? "\\" : YEN_SYMBOL);
}
private void yen() {
section("Yen");
press(ROBOT_KEYCODE_YEN_SYMBOL_JIS);
expectText(useBackslash ? "\\" : YEN_SYMBOL);
}
private void optYen() {
section("Opt+Yen");
press(ROBOT_KEYCODE_YEN_SYMBOL_JIS, ALT_DOWN_MASK);
expectText(useBackslash ? YEN_SYMBOL : "\\");
}
private void shiftYen() {
section("Shift+Yen");
press(ROBOT_KEYCODE_YEN_SYMBOL_JIS, SHIFT_DOWN_MASK);
expectText("|");
}
private void optShiftYen() {
section("Opt+Shift+Yen");
press(ROBOT_KEYCODE_YEN_SYMBOL_JIS, SHIFT_DOWN_MASK | ALT_DOWN_MASK);
expectText("|");
}
public static void main(String[] args) {
new JapaneseYenBackslashTest().run();
}
}

View File

@@ -25,15 +25,15 @@
* @test
* @summary Regression test for JBR-5173 macOS keyboard support rewrite
* @modules java.desktop/sun.lwawt.macosx
* @run main/othervm -Dcom.sun.awt.reportDeadKeysAsNormal=false InputMethodTest KeyCodesTest
* @requires (jdk.version.major >= 8 & os.family == "mac")
* @run main/othervm -Dcom.sun.awt.reportDeadKeysAsNormal=false KeyCodesTest
* @requires (jdk.version.major >= 17 & os.family == "mac")
*/
import sun.lwawt.macosx.LWCToolkit;
import static java.awt.event.KeyEvent.*;
public class KeyCodesTest implements Runnable {
public class KeyCodesTest extends TestFixture {
static private final int ROBOT_KEYCODE_BACK_QUOTE_ISO = 0x2000132;
static private final int ROBOT_KEYCODE_RIGHT_COMMAND = 0x2000036;
static private final int ROBOT_KEYCODE_RIGHT_SHIFT = 0x200003C;
@@ -44,8 +44,9 @@ public class KeyCodesTest implements Runnable {
static private final int ROBOT_KEYCODE_NUMPAD_ENTER = 0x200004C;
static private final int ROBOT_KEYCODE_NUMPAD_EQUALS = 0x2000051;
static private final int VK_SECTION = 0x01000000+0x00A7;
@Override
public void run() {
public void test() throws Exception {
// ordinary non-letter character with VK_ key codes
verify("!", VK_EXCLAMATION_MARK, "com.apple.keylayout.French-PC", VK_SLASH);
verify("\"", VK_QUOTEDBL, "com.apple.keylayout.French-PC", VK_3);
@@ -76,7 +77,6 @@ public class KeyCodesTest implements Runnable {
verify("{", VK_BRACELEFT, "com.apple.keylayout.LatinAmerican", VK_QUOTE);
verify("}", VK_BRACERIGHT, "com.apple.keylayout.LatinAmerican", VK_BACK_SLASH);
verify("\u00a1", VK_INVERTED_EXCLAMATION_MARK, "com.apple.keylayout.Spanish-ISO", VK_EQUALS);
// TODO: figure out which keyboard layout has VK_EURO_SIGN as a key on the primary layer
verify(" ", VK_SPACE, "com.apple.keylayout.ABC", VK_SPACE);
// control characters
@@ -131,15 +131,6 @@ public class KeyCodesTest implements Runnable {
verify("", VK_CONTROL, "com.apple.keylayout.ABC", ROBOT_KEYCODE_RIGHT_CONTROL, VK_UNDEFINED, KEY_LOCATION_RIGHT, CTRL_DOWN_MASK);
verify("", VK_SHIFT, "com.apple.keylayout.ABC", VK_SHIFT, VK_UNDEFINED, KEY_LOCATION_LEFT, SHIFT_DOWN_MASK);
verify("", VK_SHIFT, "com.apple.keylayout.ABC", ROBOT_KEYCODE_RIGHT_SHIFT, VK_UNDEFINED, KEY_LOCATION_RIGHT, SHIFT_DOWN_MASK);
// TODO: disabled the test because it was flapping and isn't very useful anyways
// // duplicate key codes: Vietnamese ANSI_6 / ANSI_9
// verify(" \u0309", 0x1000000+0x0309, "com.apple.keylayout.Vietnamese", VK_6);
// verify(" \u0323", 0x1000000+0x0323, "com.apple.keylayout.Vietnamese", VK_9);
//
// // duplicated key codes (dead): Apache ANSI_LeftBracket / ANSI_RightBracket
// verify("\u02db", VK_DEAD_OGONEK, "com.apple.keylayout.Apache", VK_OPEN_BRACKET, 0x1000000+0x02DB, KEY_LOCATION_STANDARD, 0);
// verify("\u02db\u0301", 0x1000000+0x0301, "com.apple.keylayout.Apache", VK_CLOSE_BRACKET);
}
private void verify(String typed, int vk, String layout, int key, int charKeyCode, int location, int modifiers) {
@@ -148,19 +139,23 @@ public class KeyCodesTest implements Runnable {
return;
}
char ch = (typed.length() == 1) ? typed.charAt(0) : 0;
InputMethodTest.section("Key code test: " + vk + ", layout: " + layout + ", char: " + String.format("U+%04X", (int)ch));
InputMethodTest.layout(layout);
InputMethodTest.type(key, 0);
InputMethodTest.expectText(typed);
section("Key code test: " + vk + ", layout: " + layout + ", char: " + String.format("U+%04X", (int)ch));
layout(layout);
press(key);
expectText(typed);
if (ch != 0) {
InputMethodTest.expectTrue(getExtendedKeyCodeForChar(ch) == charKeyCode, "getExtendedKeyCodeForChar");
expectEquals(getExtendedKeyCodeForChar(ch), charKeyCode);
}
InputMethodTest.expectKeyPress(vk, location, modifiers, true);
expectKeyPressed(vk, modifiers);
}
private void verify(String typed, int vk, String layout, int key) {
verify(typed, vk, layout, key, vk, KEY_LOCATION_STANDARD, 0);
}
public static void main(String[] args) {
new KeyCodesTest().run();
}
}

View File

@@ -25,9 +25,9 @@
* @test
* @summary Regression test for JBR-5469 Something weird going on with Cmd+Backtick / Cmd+Dead Grave
* @modules java.desktop/sun.lwawt.macosx
* @run main/othervm -Dapple.awt.captureNextAppWinKey=true -Dcom.sun.awt.reportDeadKeysAsNormal=false InputMethodTest NextAppWinKeyTestDead
* @run main/othervm -Dapple.awt.captureNextAppWinKey=true -Dcom.sun.awt.reportDeadKeysAsNormal=true InputMethodTest NextAppWinKeyTestNormal
* @requires (jdk.version.major >= 8 & os.family == "mac")
* @run main/othervm -Dapple.awt.captureNextAppWinKey=true -Dcom.sun.awt.reportDeadKeysAsNormal=false NextAppWinKeyTest dead
* @run main/othervm -Dapple.awt.captureNextAppWinKey=true -Dcom.sun.awt.reportDeadKeysAsNormal=true NextAppWinKeyTest normal
* @requires (jdk.version.major >= 17 & os.family == "mac")
*/
import javax.swing.*;
@@ -37,7 +37,7 @@ import java.awt.event.FocusListener;
import static java.awt.event.KeyEvent.*;
public class NextAppWinKeyTest implements Runnable {
public class NextAppWinKeyTest extends TestFixture {
private final boolean deadKeys;
public NextAppWinKeyTest(boolean deadKeys) {
@@ -45,38 +45,48 @@ public class NextAppWinKeyTest implements Runnable {
}
@Override
public void run() {
var extraFrame = new JFrame();
extraFrame.setAutoRequestFocus(false);
extraFrame.setSize(300, 300);
extraFrame.setLocation(100, 100);
extraFrame.setVisible(true);
extraFrame.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent focusEvent) {
InputMethodTest.fail("Focus switched to the other window");
}
public void test() throws Exception {
final JFrame[] extraFrame = new JFrame[1];
SwingUtilities.invokeAndWait(() -> {
extraFrame[0] = new JFrame();
extraFrame[0].setAutoRequestFocus(false);
extraFrame[0].setSize(300, 300);
extraFrame[0].setLocation(100, 100);
extraFrame[0].setVisible(true);
extraFrame[0].addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent focusEvent) {
expect(false, "Focus switched to the other window");
}
@Override
public void focusLost(FocusEvent focusEvent) {}
@Override
public void focusLost(FocusEvent focusEvent) {}
});
});
InputMethodTest.section("ABC");
InputMethodTest.layout("com.apple.keylayout.ABC");
InputMethodTest.type(VK_BACK_QUOTE, META_DOWN_MASK);
InputMethodTest.expectKeyPress(VK_BACK_QUOTE, KEY_LOCATION_STANDARD, META_DOWN_MASK, false);
section("ABC");
layout("com.apple.keylayout.ABC");
press(VK_BACK_QUOTE, META_DOWN_MASK);
expectKeyPressed(VK_BACK_QUOTE, META_DOWN_MASK);
InputMethodTest.section("US-Intl");
InputMethodTest.layout("com.apple.keylayout.USInternational-PC");
InputMethodTest.type(VK_BACK_QUOTE, META_DOWN_MASK);
InputMethodTest.expectKeyPress(deadKeys ? VK_DEAD_GRAVE : VK_BACK_QUOTE, KEY_LOCATION_STANDARD, META_DOWN_MASK, false);
section("US-Intl");
layout("com.apple.keylayout.USInternational-PC");
press(VK_BACK_QUOTE, META_DOWN_MASK);
expectKeyPressed(deadKeys ? VK_DEAD_GRAVE : VK_BACK_QUOTE, META_DOWN_MASK);
InputMethodTest.section("French");
InputMethodTest.layout("com.apple.keylayout.French");
InputMethodTest.type(VK_BACK_SLASH, META_DOWN_MASK);
InputMethodTest.expectKeyPress(deadKeys ? VK_DEAD_GRAVE : VK_BACK_QUOTE, KEY_LOCATION_STANDARD, META_DOWN_MASK, false);
section("French");
layout("com.apple.keylayout.French");
press(VK_BACK_SLASH, META_DOWN_MASK);
expectKeyPressed(deadKeys ? VK_DEAD_GRAVE : VK_BACK_QUOTE, META_DOWN_MASK);
extraFrame.setVisible(false);
extraFrame.dispose();
SwingUtilities.invokeAndWait(() -> {
extraFrame[0].setVisible(false);
extraFrame[0].dispose();
});
}
public static void main(String[] args) {
assert args.length == 1;
new NextAppWinKeyTest(args[0].equals("dead")).run();
}
}

View File

@@ -1,68 +0,0 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* 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
* @summary Regression test for IDEA-221385: Cannot input with half-width punctuation.
* @modules java.desktop/sun.lwawt.macosx
* @run main InputMethodTest PinyinFullWidthPunctuationTest
* @requires (jdk.version.major >= 8 & os.family == "mac")
*/
import static java.awt.event.KeyEvent.*;
public class PinyinFullWidthPunctuationTest implements Runnable {
@Override
public void run() {
InputMethodTest.layout("com.apple.inputmethod.SCIM.ITABC");
InputMethodTest.setUseHalfWidthPunctuation(false);
InputMethodTest.section("comma");
InputMethodTest.type(VK_COMMA, 0);
InputMethodTest.expectText("\uff0c");
InputMethodTest.section("period");
InputMethodTest.type(VK_PERIOD, 0);
InputMethodTest.expectText("\u3002");
InputMethodTest.section("question mark");
InputMethodTest.type(VK_SLASH, SHIFT_DOWN_MASK);
InputMethodTest.expectText("\uff1f");
InputMethodTest.section("semicolon");
InputMethodTest.type(VK_SEMICOLON, 0);
InputMethodTest.expectText("\uff1b");
InputMethodTest.section("colon");
InputMethodTest.type(VK_SEMICOLON, SHIFT_DOWN_MASK);
InputMethodTest.expectText("\uff1a");
InputMethodTest.section("left square bracket");
InputMethodTest.type(VK_OPEN_BRACKET, 0);
InputMethodTest.expectText("\u3010");
InputMethodTest.section("right square bracket");
InputMethodTest.type(VK_CLOSE_BRACKET, 0);
InputMethodTest.expectText("\u3011");
}
}

View File

@@ -1,68 +0,0 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* 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
* @summary Regression test for IDEA-221385: Cannot input with half-width punctuation.
* @modules java.desktop/sun.lwawt.macosx
* @run main InputMethodTest PinyinHalfWidthPunctuationTest
* @requires (jdk.version.major >= 8 & os.family == "mac")
*/
import static java.awt.event.KeyEvent.*;
public class PinyinHalfWidthPunctuationTest implements Runnable {
@Override
public void run() {
InputMethodTest.layout("com.apple.inputmethod.SCIM.ITABC");
InputMethodTest.setUseHalfWidthPunctuation(true);
InputMethodTest.section("comma");
InputMethodTest.type(VK_COMMA, 0);
InputMethodTest.expectText(",");
InputMethodTest.section("period");
InputMethodTest.type(VK_PERIOD, 0);
InputMethodTest.expectText(".");
InputMethodTest.section("question mark");
InputMethodTest.type(VK_SLASH, SHIFT_DOWN_MASK);
InputMethodTest.expectText("?");
InputMethodTest.section("semicolon");
InputMethodTest.type(VK_SEMICOLON, 0);
InputMethodTest.expectText(";");
InputMethodTest.section("colon");
InputMethodTest.type(VK_SEMICOLON, SHIFT_DOWN_MASK);
InputMethodTest.expectText(":");
InputMethodTest.section("left square bracket");
InputMethodTest.type(VK_OPEN_BRACKET, 0);
InputMethodTest.expectText("[");
InputMethodTest.section("right square bracket");
InputMethodTest.type(VK_CLOSE_BRACKET, 0);
InputMethodTest.expectText("]");
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* 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
* @summary Regression test for IDEA-221385: Cannot input with half-width punctuation.
* @modules java.desktop/sun.lwawt.macosx
* @run main PinyinPunctuationTest
* @requires (jdk.version.major >= 17 & os.family == "mac")
*/
import java.util.Arrays;
import java.util.List;
import static java.awt.event.KeyEvent.*;
public class PinyinPunctuationTest extends TestFixture {
record Punctuation(int keyCode, int mods, String halfWidth, String fullWidth) {}
List<Punctuation> punctuationList = Arrays.asList(
new Punctuation(VK_COMMA, 0, ",", "\uff0c"),
new Punctuation(VK_PERIOD, 0, ".", "\u3002"),
new Punctuation(VK_SLASH, SHIFT_DOWN_MASK, "?", "\uff1f"),
new Punctuation(VK_SEMICOLON, 0, ";", "\uff1b"),
new Punctuation(VK_SEMICOLON, SHIFT_DOWN_MASK, ":", "\uff1a"),
new Punctuation(VK_OPEN_BRACKET, 0, "[", "\u3010"),
new Punctuation(VK_CLOSE_BRACKET, 0, "]", "\u3011")
);
private void testQuotes(String name) {
section(name + ": single quotes");
press(VK_QUOTE);
press(VK_SPACE);
press(VK_QUOTE);
expectText("\u2018 \u2019");
section(name + ": double quotes");
press(VK_QUOTE, SHIFT_DOWN_MASK);
press(VK_SPACE);
press(VK_QUOTE, SHIFT_DOWN_MASK);
expectText("\u201c \u201d");
}
@Override
public void test() throws Exception {
layout("com.apple.inputmethod.SCIM.ITABC");
chineseUseHalfWidthPunctuation(true);
for (var punctuation : punctuationList) {
var punctuationName = punctuation.halfWidth;
section("half-width: " + punctuationName);
press(punctuation.keyCode, punctuation.mods);
expectText(punctuation.halfWidth);
}
testQuotes("half-width");
chineseUseHalfWidthPunctuation(false);
for (var punctuation : punctuationList) {
var punctuationName = punctuation.halfWidth; // halfWidth is intended here
section("full-width: " + punctuationName);
press(punctuation.keyCode, punctuation.mods);
expectText(punctuation.fullWidth);
}
testQuotes("full-width");
}
public static void main(String[] args) {
new PinyinPunctuationTest().run();
}
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* 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
* @summary Regression test for IDEA-271898: Cannot enter Chinese full-corner single and double quotes (IDEA: macOS Intel version)
* @requires (jdk.version.major >= 8 & os.family == "mac")
* @modules java.desktop/sun.lwawt.macosx
* @run main InputMethodTest PinyinQuotesTest
*/
import static java.awt.event.KeyEvent.*;
public class PinyinQuotesTest implements Runnable {
@Override
public void run() {
InputMethodTest.layout("com.apple.inputmethod.SCIM.ITABC");
singleQuotes();
doubleQuotes();
}
private void singleQuotes() {
InputMethodTest.section("Single quotes");
// type the following: ' '
InputMethodTest.type(VK_QUOTE, 0);
InputMethodTest.type(VK_SPACE, 0);
InputMethodTest.type(VK_QUOTE, 0);
InputMethodTest.expectText("\u2018 \u2019");
}
private void doubleQuotes() {
InputMethodTest.section("Double quotes");
// type the following: " "
InputMethodTest.type(VK_QUOTE, SHIFT_DOWN_MASK);
InputMethodTest.type(VK_SPACE, 0);
InputMethodTest.type(VK_QUOTE, SHIFT_DOWN_MASK);
InputMethodTest.expectText("\u201c \u201d");
}
}

View File

@@ -1,114 +0,0 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* 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
* @summary Regression test for JBR-5309: Minor keyboard inconsistencies on macOS
* @requires (jdk.version.major >= 8 & os.family == "mac")
* @modules java.desktop/sun.lwawt.macosx
* @run main InputMethodTest RomajiYenTest
* @run main InputMethodTest RomajiYenBackslashTest
*/
import static java.awt.event.KeyEvent.*;
public class RomajiYenTest implements Runnable {
private final boolean isBackslash;
static private final int ROBOT_KEYCODE_YEN_SYMBOL_JIS = 0x200025D;
static private final String YEN_SYMBOL = "\u00a5";
static private final String RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK = "\u00bb";
public RomajiYenTest(boolean isBackslash) {
this.isBackslash = isBackslash;
}
@Override
public void run() {
InputMethodTest.setUseBackslashInsteadOfYen(isBackslash);
InputMethodTest.setRomajiLayout("com.apple.keylayout.ABC");
InputMethodTest.layout("com.apple.inputmethod.Kotoeri.RomajiTyping.Roman");
backslash();
optBackslash();
shiftBackslash();
optShiftBackslash();
optY();
yen();
optYen();
shiftYen();
optShiftYen();
}
private void backslash() {
InputMethodTest.section("Backslash");
InputMethodTest.type(VK_BACK_SLASH, 0);
InputMethodTest.expectText(isBackslash ? "\\" : YEN_SYMBOL);
}
private void optBackslash() {
InputMethodTest.section("Opt+Backslash");
InputMethodTest.type(VK_BACK_SLASH, ALT_DOWN_MASK);
InputMethodTest.expectText(isBackslash ? YEN_SYMBOL : "\\");
}
private void shiftBackslash() {
InputMethodTest.section("Shift+Backslash");
InputMethodTest.type(VK_BACK_SLASH, SHIFT_DOWN_MASK);
InputMethodTest.expectText("|");
}
private void optShiftBackslash() {
InputMethodTest.section("Opt+Shift+Backslash");
InputMethodTest.type(VK_BACK_SLASH, SHIFT_DOWN_MASK | ALT_DOWN_MASK);
InputMethodTest.expectText(RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK);
}
private void optY() {
InputMethodTest.section("Opt+Y");
InputMethodTest.type(VK_Y, ALT_DOWN_MASK);
InputMethodTest.expectText(isBackslash ? "\\" : YEN_SYMBOL);
}
private void yen() {
InputMethodTest.section("Yen");
InputMethodTest.type(ROBOT_KEYCODE_YEN_SYMBOL_JIS, 0);
InputMethodTest.expectText(isBackslash ? "\\" : YEN_SYMBOL);
}
private void optYen() {
InputMethodTest.section("Opt+Yen");
InputMethodTest.type(ROBOT_KEYCODE_YEN_SYMBOL_JIS, ALT_DOWN_MASK);
InputMethodTest.expectText(isBackslash ? YEN_SYMBOL : "\\");
}
private void shiftYen() {
InputMethodTest.section("Shift+Yen");
InputMethodTest.type(ROBOT_KEYCODE_YEN_SYMBOL_JIS, SHIFT_DOWN_MASK);
InputMethodTest.expectText("|");
}
private void optShiftYen() {
InputMethodTest.section("Opt+Shift+Yen");
InputMethodTest.type(ROBOT_KEYCODE_YEN_SYMBOL_JIS, SHIFT_DOWN_MASK | ALT_DOWN_MASK);
InputMethodTest.expectText("|");
}
}

View File

@@ -0,0 +1,497 @@
/*
* Copyright 2024 JetBrains s.r.o.
* 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 sun.lwawt.macosx.LWCToolkit;
import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.List;
import static java.awt.event.KeyEvent.KEY_PRESSED;
import static java.awt.event.KeyEvent.VK_ESCAPE;
public abstract class TestFixture {
protected JFrame frame;
protected Robot robot;
protected JTextArea inputArea;
private JLabel testNameLabel;
private String testName;
private String sectionName;
private JTextArea testLogArea;
private JScrollPane testLogAreaScroll;
private List<KeyEvent> keyEvents = new ArrayList<>();
private boolean success = true;
record DomainKey(String domain, String key) {}
private HashMap<DomainKey, String> oldDefaultValues = new HashMap<>();
private String initialLayout;
private Set<String> addedKeyboardLayouts = new HashSet<>();
private void setUp() throws Exception {
SwingUtilities.invokeAndWait(() -> {
frame = new JFrame("Test");
frame.setLayout(new BorderLayout());
var size = new Dimension(800, 600);
frame.setMinimumSize(size);
frame.setSize(size);
frame.setLocationRelativeTo(null);
var panel = new JPanel();
panel.setLayout(new GridBagLayout());
frame.add(panel, BorderLayout.CENTER);
testNameLabel = new JLabel();
inputArea = new JTextArea();
testLogArea = new JTextArea();
testLogArea.setEditable(false);
testLogAreaScroll = new JScrollPane(testLogArea);
inputArea.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
handleKeyEvent(e);
}
@Override
public void keyPressed(KeyEvent e) {
handleKeyEvent(e);
}
@Override
public void keyReleased(KeyEvent e) {
handleKeyEvent(e);
}
});
var constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.weightx = 1.0;
constraints.weighty = 0.0;
constraints.anchor = GridBagConstraints.NORTH;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.ipadx = 10;
constraints.ipady = 10;
panel.add(testNameLabel, constraints);
constraints.gridy++;
panel.add(inputArea, constraints);
for (var component : getExtraComponents()) {
constraints.gridy++;
panel.add(component, constraints);
}
constraints.gridy++;
constraints.weighty = 1.0;
constraints.anchor = GridBagConstraints.SOUTH;
constraints.fill = GridBagConstraints.BOTH;
panel.add(testLogAreaScroll, constraints);
frame.setVisible(true);
frame.setFocusable(true);
frame.requestFocus();
});
robot = new Robot();
robot.setAutoDelay(50);
robot.delay(100);
try {
initialLayout = LWCToolkit.getKeyboardLayoutId();
} catch (Exception e) {
e.printStackTrace();
}
try {
Toolkit.getDefaultToolkit().setLockingKeyState(KeyEvent.VK_CAPS_LOCK, false);
} catch (Exception e) {
e.printStackTrace();
}
}
private void tearDown() {
try {
Toolkit.getDefaultToolkit().setLockingKeyState(KeyEvent.VK_CAPS_LOCK, false);
} catch (Exception e) {
e.printStackTrace();
}
for (var entry : oldDefaultValues.entrySet()) {
try {
if (entry.getValue() == null) {
deleteDefaultImpl(entry.getKey().domain, entry.getKey().key);
} else {
writeDefaultImpl(entry.getKey().domain, entry.getKey().key, entry.getValue());
}
} catch (Exception e) {
e.printStackTrace();
}
}
for (String layout : addedKeyboardLayouts) {
try {
LWCToolkit.disableKeyboardLayout(layout);
} catch (Exception e) {
e.printStackTrace();
}
}
if (initialLayout != null) {
try {
LWCToolkit.switchKeyboardLayout(initialLayout);
} catch (Exception e) {
e.printStackTrace();
}
}
try {
robot.delay(100);
SwingUtilities.invokeAndWait(() -> {
frame.dispose();
});
robot.delay(100);
} catch (Exception e) {
e.printStackTrace();
}
}
private void handleKeyEvent(KeyEvent e) {
keyEvents.add(e);
}
final protected void layout(String name) {
List<String> layouts = new ArrayList<>();
if (name.matches("com\\.apple\\.inputmethod\\.(SCIM|TCIM|TYIM|Korean|VietnameseIM|Kotoeri\\.\\w+)\\.\\w+")) {
layouts.add(name.replaceFirst("\\.\\w+$", ""));
}
layouts.add(name);
for (String layout : layouts) {
if (!LWCToolkit.isKeyboardLayoutEnabled(layout)) {
LWCToolkit.enableKeyboardLayout(layout);
addedKeyboardLayouts.add(layout);
}
}
LWCToolkit.switchKeyboardLayout(name);
robot.delay(100);
}
private void updateTestNameLabel() {
try {
SwingUtilities.invokeAndWait(() -> {
testNameLabel.setText(testName + ": " + sectionName);
});
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
private void prepareForNextSection() {
inputArea.requestFocusInWindow();
inputArea.enableInputMethods(false);
robot.delay(50);
robot.keyPress(VK_ESCAPE);
robot.keyRelease(VK_ESCAPE);
inputArea.enableInputMethods(true);
robot.delay(50);
inputArea.setText("");
keyEvents.clear();
}
private void logMessage(String message) {
System.err.println(message);
try {
SwingUtilities.invokeAndWait(() -> {
testLogArea.append(message + "\n");
testLogArea.setCaretPosition(testLogArea.getDocument().getLength());
});
} catch (Exception e) {
e.printStackTrace();
}
}
private String keyWithModifiersToString(int keyCode, int modifiers) {
StringBuilder message = new StringBuilder();
if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) {
message.append("Opt+");
}
if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) {
message.append("Ctrl+");
}
if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) {
message.append("Shift+");
}
if ((modifiers & InputEvent.META_DOWN_MASK) != 0) {
message.append("Cmd+");
}
message.append(KeyEvent.getKeyText(keyCode));
return message.toString();
}
private String readDefaultImpl(String domain, String key) {
try {
var exec = Runtime.getRuntime().exec(new String[]{"defaults", "read", domain, key});
var exitCode = exec.waitFor();
if (exitCode != 0) {
return null;
}
var contents = exec.inputReader(StandardCharsets.UTF_8).readLine().strip();
return contents;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("internal error");
}
}
private void writeDefaultImpl(String domain, String key, String value) {
try {
Runtime.getRuntime().exec(new String[]{"defaults", "write", domain, key, value}).waitFor();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("internal error");
}
}
private void deleteDefaultImpl(String domain, String key) {
try {
Runtime.getRuntime().exec(new String[]{"defaults", "delete", domain, key}).waitFor();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("internal error");
}
}
private void setDefault(String domain, String key, String value) {
var domainKey = new DomainKey(domain, key);
if (!oldDefaultValues.containsKey(domainKey)) {
oldDefaultValues.put(domainKey, readDefaultImpl(domain, key));
}
writeDefaultImpl(domain, key, value);
robot.delay(50);
}
private void killProcess(String name) {
try {
Runtime.getRuntime().exec(new String[]{"killall", "-9", "-m", name}).waitFor();
} catch (Exception exc) {
exc.printStackTrace();
throw new RuntimeException("internal error");
}
}
private void restartJapaneseIM() {
killProcess("JapaneseIM");
robot.delay(1000);
}
final protected void chineseUseHalfWidthPunctuation(boolean value) {
setDefault("com.apple.inputmethod.CoreChineseEngineFramework", "usesHalfwidthPunctuation", value ? "1" : "0");
}
final protected void japaneseUseBackslash(boolean value) {
setDefault("com.apple.inputmethod.Kotoeri", "JIMPrefCharacterForYenKey", value ? "1" : "0");
restartJapaneseIM();
}
final protected void romajiLayout(String layout) {
setDefault("com.apple.inputmethod.Kotoeri", "JIMPrefRomajiKeyboardLayoutKey", layout);
restartJapaneseIM();
}
final protected void beginTest(String name) {
testName = name;
sectionName = "";
updateTestNameLabel();
prepareForNextSection();
logMessage("Begin test " + name);
}
final protected void section(String name) {
sectionName = name;
updateTestNameLabel();
prepareForNextSection();
logMessage("Begin section " + name);
}
final protected void press(int keyCode, int modifiers) {
List<Integer> modKeys = new ArrayList<>();
if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) {
modKeys.add(KeyEvent.VK_ALT);
}
if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) {
modKeys.add(KeyEvent.VK_CONTROL);
}
if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) {
modKeys.add(KeyEvent.VK_SHIFT);
}
if ((modifiers & InputEvent.META_DOWN_MASK) != 0) {
modKeys.add(KeyEvent.VK_META);
}
logMessage("Key press: " + keyWithModifiersToString(keyCode, modifiers));
for (var modKey : modKeys) {
robot.keyPress(modKey);
}
robot.keyPress(keyCode);
robot.keyRelease(keyCode);
for (var modKey : modKeys) {
robot.keyRelease(modKey);
}
}
final protected void press(int keyCode) {
press(keyCode, 0);
}
final protected void delay(int ms) {
robot.delay(ms);
}
final protected String getText() {
String[] text = {null};
try {
SwingUtilities.invokeAndWait(() -> {
text[0] = inputArea.getText();
});
} catch (Exception e) {
e.printStackTrace();
}
return text[0];
}
final protected void expect(boolean value, String comment) {
if (value) {
logMessage(comment + ": OK");
} else {
success = false;
logMessage(comment + ": FAIL");
}
}
final protected void expectEquals(Object left, Object right) {
if (Objects.equals(left, right)) {
logMessage(left + " == " + right + ": OK");
} else {
success = false;
logMessage(left + " != " + right + ": FAIL");
}
}
final protected void expectNotEquals(Object left, Object right) {
if (Objects.equals(left, right)) {
success = false;
logMessage(left + " == " + right + ": FAIL");
} else {
logMessage(left + " != " + right + ": OK");
}
}
final protected void expectText(String text) {
try {
expectEquals(text, getText());
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
final protected void expectKeyPressed(int key, int modifiers) {
var pressed = keyEvents.stream().filter(e -> e.getID() == KEY_PRESSED).toList();
var foundKey = false;
for (var e : pressed) {
if (e.getKeyCode() == key) {
expectEquals(e.getModifiersEx(), modifiers);
foundKey = true;
}
}
expect(foundKey, "expected key press: " + keyWithModifiersToString(key, modifiers));
}
final protected void setCapsLockState(boolean desiredState) {
Toolkit.getDefaultToolkit().setLockingKeyState(KeyEvent.VK_CAPS_LOCK, desiredState);
robot.delay(250);
}
final public void run() {
try {
setUp();
beginTest(this.getClass().getName());
test();
} catch (Exception e) {
e.printStackTrace();
success = false;
}
try {
tearDown();
} catch (Exception e) {
e.printStackTrace();
}
if (success) {
System.out.println("TEST PASSED");
} else {
throw new RuntimeException("TEST FAILED");
}
}
protected List<JComponent> getExtraComponents() {
return new ArrayList<>();
}
abstract protected void test() throws Exception;
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright (c) 2000-2023 JetBrains s.r.o.
* 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
* @summary Regression test for JBR-5558 macOS keyboard rewrite 2
* @requires (jdk.version.major >= 8 & os.family == "mac")
* @modules java.desktop/sun.lwawt.macosx
* @run main InputMethodTest UnderlyingLayoutQWERTYTest
* @run main InputMethodTest UnderlyingLayoutQWERTZTest
*/
import static java.awt.event.KeyEvent.*;
public class UnderlyingLayoutTest implements Runnable {
private final boolean isQwertz;
public UnderlyingLayoutTest(boolean isQwertz) {
this.isQwertz = isQwertz;
}
@Override
public void run() {
if (isQwertz) {
InputMethodTest.setRomajiLayout("com.apple.keylayout.ABC-QWERTZ");
qwertz("com.apple.keylayout.German");
qwertz("com.apple.keylayout.ABC-QWERTZ");
qwertz("com.apple.keylayout.Polish");
qwertz("com.apple.inputmethod.Kotoeri.RomajiTyping.Roman");
qwertz("com.apple.inputmethod.Kotoeri.RomajiTyping.Japanese");
qwertz("com.apple.inputmethod.Kotoeri.KanaTyping.Roman");
// qwerty("com.apple.inputmethod.Kotoeri.KanaTyping.Japanese");
} else {
InputMethodTest.setRomajiLayout("com.apple.keylayout.ABC");
qwerty("com.apple.keylayout.US");
qwerty("com.apple.keylayout.ABC");
qwerty("com.apple.keylayout.Russian");
qwerty("com.apple.inputmethod.Kotoeri.RomajiTyping.Roman");
qwerty("com.apple.inputmethod.Kotoeri.RomajiTyping.Japanese");
qwerty("com.apple.inputmethod.Kotoeri.KanaTyping.Roman");
qwerty("com.apple.inputmethod.Kotoeri.KanaTyping.Japanese");
}
}
private void qwerty(String layout) {
testImpl(layout, VK_Y);
}
private void qwertz(String layout) {
testImpl(layout, VK_Z);
}
private void testImpl(String layout, int vkY) {
InputMethodTest.section("Cmd " + layout);
InputMethodTest.layout(layout);
InputMethodTest.type(VK_Y, META_DOWN_MASK);
InputMethodTest.expectKeyPress(vkY, KEY_LOCATION_STANDARD, META_DOWN_MASK, false);
InputMethodTest.section("Ctrl " + layout);
InputMethodTest.type(VK_Y, CTRL_DOWN_MASK);
InputMethodTest.expectKeyPress(vkY, KEY_LOCATION_STANDARD, CTRL_DOWN_MASK, false);
}
}

View File

@@ -1337,8 +1337,6 @@ javax/swing/text/html/7189299/bug7189299.java JBR-4880 windows-all
jb/java/jcef/HandleJSQueryTest3314.sh JBR-4866 linux-all
jb/java/jcef/HwFacadeWindowNoFrontTest.java
jb/sun/awt/macos/InputMethodTest/FocusMoveUncommitedCharactersTest.java JBR-5765 macosx-all
jb/sun/awt/macos/InputMethodTest/PinyinCapsLockTest.java JBR-5282 macosx-all
jb/sun/awt/macos/KeyPressAndHoldTest.java JBR-4901 macosx-all
jb/sun/awt/macos/NationalLayoutTest/Layout_ABC.java JBR-4933 macosx-all
jb/sun/awt/macos/NationalLayoutTest/Layout_FRENCH_PC.java JBR-4933 macosx-all