我正在使用 java.awt.Robot 对我的 Swing 应用程序进行集成测试,但我无法按正确的顺序运行我的操作。我如何告诉调用 robot.mousePressed(...) 的线程阻塞,直到 Swing 完成调度该事件?显然,robot.setAutoWaitForIdle(true) 没有任何好处。
这是我的演示。我期待“机器人完成!”消息总是在“Action finished blocking.”之后出现,但它通常发生得太快了。
import java.awt.AWTException;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.sql.Date;
import java.text.DateFormat;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class RobotWaitForIdleDemo {
/**
* Create the device that contains the given point in screen coordinates.
* Robot has to be constructed differently for each monitor.
*/
public static GraphicsDevice getDevice(Point p) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
// Search the devices for the one that draws the specified point.
for (GraphicsDevice device : gs) {
GraphicsConfiguration configuration = device.getDefaultConfiguration();
Rectangle bounds = configuration.getBounds();
if (bounds.contains(p)) {
return device;
}
}
return null;
}
public static final Logger logger = Logger.getLogger(RobotWaitForIdleDemo.class.getName());
public static void main(String[] args) {
LogManager.getLogManager().reset();
Formatter formatter = new Formatter() {
@Override
public String format(LogRecord arg0) {
Date date = new Date(arg0.getMillis());
DateFormat.getTimeInstance().format(date);
return String.format("%s %s %s %s%n",
DateFormat.getTimeInstance().format(date),
arg0.getLoggerName(),
arg0.getLevel(),
arg0.getMessage());
}
};
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(formatter);
logger.addHandler(consoleHandler);
final JFrame jframe = new JFrame("Robot experiment");
GroupLayout groupLayout = new GroupLayout(jframe.getContentPane());
final JButton jbutton = new JButton("Click me!");
jbutton.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
// Simulate a heavy Swing event handler.
logger.info("(swing thread) Action starting to block...");
try {
Thread.sleep(500);
} catch (InterruptedException e1) {}
logger.info("(swing thread) Action finished blocking.");
}
});
JButton tryAgainBUtton = new JButton("Automatically click above button.");
tryAgainBUtton.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
new Thread(new Runnable() {
@Override public void run() {
try {
Point point = new Point(jbutton.getWidth()/2,jbutton.getHeight()/2);
SwingUtilities.convertPointToScreen(point, jbutton);
GraphicsDevice device = getDevice(point);
Point offset = device.getDefaultConfiguration().getBounds().getLocation();
Robot robot = new Robot(device);
robot.setAutoWaitForIdle(true);
robot.setAutoDelay(30);
robot.mouseMove(point.x - offset.x, point.y - offset.y);
String threadName = Thread.currentThread().getName();
logger.info(String.format("(%s) robot.mousePress(%d)", threadName, InputEvent.BUTTON1_MASK));
robot.mousePress(InputEvent.BUTTON1_MASK);
logger.info(String.format("(%s) robot.mouseRelease(%d)", threadName, InputEvent.BUTTON1_MASK));
robot.mouseRelease(InputEvent.BUTTON1_MASK);
logger.info(String.format("(%s) robot finished!", threadName, InputEvent.BUTTON1_MASK));
} catch (AWTException ex) {
ex.printStackTrace();
}
}
}, "robot thread").start();
}
});
jframe.getContentPane().setLayout(groupLayout);
groupLayout.setAutoCreateGaps(true);
groupLayout.setAutoCreateContainerGaps(true);
groupLayout.setVerticalGroup(
groupLayout.createSequentialGroup()
.addComponent(jbutton)
.addComponent(tryAgainBUtton));
groupLayout.setHorizontalGroup(
groupLayout.createParallelGroup()
.addComponent(jbutton)
.addComponent(tryAgainBUtton) );
jframe.setSize(300, 300);
jframe.setVisible(true);
jframe.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
}
我在 Ubuntu 上运行 Java 1.6。
最佳答案
也许这个可以帮助你,注意没有在 Java7 中测试
您可以在 isEventDispatchThread() 的每个步骤中进行测试
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import sun.awt.SunToolkit;
public class TestMenu {
/**
* Without a delay, SunToolkit may encounter a problem in SunToolkit (at
* least in JDK 6, where the drop down size problem is not present).
*
* Note: SunToolkit also has some mechanism to delay, but I forgot how it
* worked.
*
* <pre>
* Exception in thread "main" sun.awt.SunToolkit$InfiniteLoop
* at sun.awt.SunToolkit.realSync(Unknown Source)
* at TestMenu.syncAndDelay(TestMenu.java:172)
* at TestMenu.click(TestMenu.java:88)
* at TestMenu.moveAndClickCenter(TestMenu.java:150)
* at TestMenu.main(TestMenu.java:45)
* </pre>
*
* As a bonus, the delay makes the scenario better visible for the human
* eye.
*/
private static int delay = 500;
private static JMenu[] menus = new JMenu[5];
private static Dimension[] parentSizes;
private static Robot robot;
private static SunToolkit toolkit;
public static void main(String[] args) throws Exception {
robot = new Robot();
toolkit = (SunToolkit) Toolkit.getDefaultToolkit();
parentSizes = new Dimension[menus.length];
createGUI(); // Open the first menu. Then get the drop down size of all menu's
moveAndClickCenter(menus[0]);
for (int index = 0; index < menus.length; index++) {
parentSizes[index] = getDropDownSize(index);
}// Click the last item on the last menu.
Component item = menus[menus.length - 1].getMenuComponent(menus[menus.length - 1].getMenuComponentCount() - 1);
moveAndClickCenter(item);
// Open the last drop down again. Then get the drop down sizes once more. If size not equal to previous size, then it's a bug.
boolean bug = false;
moveAndClickCenter(menus[menus.length - 1]);
for (int index = menus.length - 1; index >= 0; index--) {
Dimension currentSize = getDropDownSize(index);
System.out.print("old: " + parentSizes[index] + ", new: " + currentSize);
if (!parentSizes[index].equals(currentSize)) {
bug = true;
System.out.println(" ERROR");
} else {
System.out.println();
}
}
if (bug) {
throw new RuntimeException("JMenu drop down size is changed for no reason.");
}
}
private static Dimension getDropDownSize(int index) throws Exception {
moveToCenter(menus[index]);
return menus[index].getMenuComponent(0).getParent().getSize();
}
private static void click() throws Exception {
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
syncAndDelay();
}
private static void createGUI() throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();// The L&F defines the drop down policy.
for (final UIManager.LookAndFeelInfo info : infos) {
if (info.getName().toLowerCase().indexOf("metal") >= 0) {
if (!UIManager.getLookAndFeel().getName().equals(info.getName())) {
try {
UIManager.setLookAndFeel(info.getClassName());
System.out.println("Attempt to set look and feel to " + info.getName());
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("Metal look and feel is the default");
}
break;
}
}
System.out.println("Testing with " + UIManager.getLookAndFeel().getName()); // Setup the GUI.
JFrame frame = new JFrame("A frame");
frame.setJMenuBar(new JMenuBar());
for (int menuIndex = 0; menuIndex < menus.length; menuIndex++) {
menus[menuIndex] = new JMenu("Menu " + menuIndex);
frame.getJMenuBar().add(menus[menuIndex]);
for (int itemIndex = 0; itemIndex <= menus.length - menuIndex; itemIndex++) {
// It seems that the problem only occurs if the drop down is displayed outside the frame at the right
// (not sure though). A rather long item name.
JMenuItem item = new JMenuItem("Menu " + menuIndex + " item " + itemIndex);
menus[menuIndex].add(item);
}
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
syncAndDelay();
}
private static void moveAndClickCenter(Component c) throws Exception {
moveToCenter(c);
click();
}
private static void moveToCenter(final Component c) throws Exception {
final Point cp = new Point();
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
Point p = new Point(c.getWidth() / 2, c.getHeight() / 2);
SwingUtilities.convertPointToScreen(p, c);
cp.setLocation(p);
}
});
robot.mouseMove(cp.x, cp.y);
syncAndDelay();
}
private static void syncAndDelay() throws Exception {
if (delay > 0) {
Thread.sleep(delay);
}
toolkit.realSync();
}
private TestMenu() {
}
}
关于java - java.awt.Robot.waitForIdle() 是否等待事件被分派(dispatch)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11042979/
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案
我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查
我的日期格式如下:"%d-%m-%Y"(例如,今天的日期为07-09-2015),我想看看是不是在过去的七天内。谁能推荐一种方法? 最佳答案 你可以这样做:require"date"Date.today-7 关于ruby-检查日期是否在过去7天内,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/32438063/
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您