【问题标题】:Fest slows down while testing with swingx jxtreetable使用 swingx jxtreetable 进行测试时,Fest 变慢了
【发布时间】:2013-06-10 15:24:56
【问题描述】:

我不知道如何解释。但我会尝试.. Fest 在使用 swingx 的 JXTreeTable 时会减速爬行。它最初并没有减速。它在一段时间内可以正常工作,但在重复相同的操作后,它会严重减慢速度。

我在 github 中为此提出了一个错误。请告诉我这是否是我做错的事情。当我尝试创建 SSCCE 时,我无法重现该问题。

不管怎样,这是一段慢下来的视频。

http://screencast.com/t/liNttCw2In0w

在 0.39 秒到 0.40 秒之间执行一组操作。这些是在 JXTreeTable 中有一行时完成的。

在时间 0.49 到记录结束时重复相同的操作,但现在表格中有 3 行,鼠标单击需要很长时间。

我附上了一张在 fest 变慢时截取的屏幕截图,试图对此进行更多解释

这是完成工作的代码:

步骤1)从树中选择一个节点如下:

JTreeFixture folioTreeFixture = importShareholders.panel("treePanel").tree("folioTree");

        folioTreeFixture.separator("~");
        folioTreeFixture.selectPath(new StringWrapper("Shareholders", true)+"~"+
             (ShareType.isEquity(shareType) ? new StringWrapper("Equity Folios", true) : new StringWrapper("Preference Folios", true))+"~"+
                new FolioTreeRep(folio.getName(),folioNo, shareType).toString());

步骤 2) 从 JXTreeTable 中搜索并选择一行

int selectRow=-1;
JTableFixture table=importShareholders.table("historyTable");
for(int i=0;i<table.rowCount();i++){
    String certificateNumber = table.cell(TableCell.row(i).column(ShareholderHistoryTable.columnIndex(ShareholderHistoryTable.CERT_NO))).value();
    String remarks=table.cell(TableCell.row(i).column(ShareholderHistoryTable.columnIndex(ShareholderHistoryTable.REMARKS))).value();
    if(StringUtils.isEmpty(remarks) && StringUtils.isNotEmpty(certificateNumber) && Integer.parseInt(certificateNumber)==certNo){
        selectRow=i;
        break;
    }
}
if(selectRow==-1){
    fail("Couldn't find certificate number to transfer");
}

步骤 3) 显示弹出菜单并单击该行

table.showPopupMenuAt(TableCell.row(selectRow).column(0)).menuItem("btnTransfer").click();

我不确定它为什么会变慢。如果有更多信息可以提供帮助,请告诉我。将不胜感激解决问题的一些帮助

我已对应用程序进行了概要分析,但没有发现任何不愉快的事情发生。我没有很多分析应用程序的经验。如果有人可以再看看这个,我将不胜感激。我使用 yourkit 对其进行了分析,并在此处上传了快照转储:

https://www.dropbox.com/s/dh976v01q9c3sgj/ImportShareholderData.shouldTransferAndSplit-2013-06-14-shutdown.snapshot.zip

任何帮助将不胜感激..

编辑:

我想我忘了提到手动操作时同样的事情。它只会随着节日而减慢。这让我相信 fest 可能存在问题?

对此感到抱歉。

编辑 2: 根据 Marcin 的要求(抱歉,Marcin 耽搁了).. 这是第一行拆分时的代码

public List<Integer> splitRowEqually(ShareType shareType, String date, int folioNo, int certNo, int... certnos) throws NoSuchFieldException,     TorqueException {
    //select a tree node
    selectFolioInTree(shareType, folioNo);
    Pause.pause(new Condition("Wait until tab is created") {
        @Override
        public boolean test() {
            return importShareholders.tabbedPane().tabTitles().length>0;
        }
    });
    //select a row on the table to split
    int row=selectRowWithCertNunber(certNo);
    List<Integer> rowsIndexes=new ArrayList<Integer>();
    JTableFixture table = importShareholders.table();
    //show popup menu on that row and select split
    table.showPopupMenuAt(row(row).column(columnIndex(TRANS_TYPE))).menuItem("btnSplit").click();
    DialogFixture splitDialog=FinderUtilities.getDialogWithTitle("Split Share Certificate");
    splitDialog.textBox("tfDateOfSplit").setText(date);
    int noOfShares= Integer.parseInt(table.cell(row(row).column(columnIndex(NO_OF_SHARES))).value());
    int distFrom= Integer.parseInt(table.cell(row(row).column(columnIndex(DIST_NO_FROM))).value());
    int distTo= Integer.parseInt(table.cell(row(row).column(columnIndex(DIST_NO_TO))).value());
    //split the row into the number of times decided by the certnos array
    int noOfSharesInEachSplit=noOfShares/certnos.length;
    for(int i=0;i<certnos.length;i++){
        int distToInSplit = distFrom + noOfSharesInEachSplit-1;
        enterSplitRowDetails(splitDialog, certnos[i], distFrom, distToInSplit<=distTo ? distToInSplit : distTo);
        distFrom=distToInSplit+1;
        rowsIndexes.add(row++);
    }
    splitDialog.button("btnSplit").click();
    return rowsIndexes;
}

//selects a node from the left hand side tree
public void selectFolioInTree(final ShareType shareType,final int folioNo) throws TorqueException {
    JTreeFixture folioTreeFixture = importShareholders.panel("treePanel").tree("folioTree");
    folioTreeFixture.separator("~");
    // I use these wrapper classes - StringWrapper and FolioTreeRep, so that I can get a html 
    // string for the tree node like <html><b>Shareholder</b></html>
    String treePath = new StringWrapper("Shareholders", true) + "~" +
            (ShareType.isEquity(shareType) ? new StringWrapper("Equity Folios", true) : new StringWrapper("Preference Folios", true)) + "~" +
            new FolioTreeRep(mapOfFolioNames.get(folioNo), folioNo, shareType).toString();
    folioTreeFixture.clickPath(treePath);
}

//search the table for a row that contains the cert no provided in the Certificate Number column.
private int selectRowWithCertNunber(int certNo) throws NoSuchFieldException {
    int selectRow=-1;
    JTableFixture table=importShareholders.table("historyTable");
    for(int i=0;i<table.rowCount();i++){
        String certificateNumber = table.cell(row(i).column(columnIndex(CERT_NO))).value();
        String remarks=table.cell(row(i).column(columnIndex(REMARKS))).value();
        if(StringUtils.isEmpty(remarks) && StringUtils.isNotEmpty(certificateNumber) 
           && Integer.parseInt(certificateNumber)==certNo){
            selectRow=i;
            break;
        }
    }
    if(selectRow==-1){
        fail("Couldn't find certificate number to transfer");
    }
    return selectRow;
}

// enter details on the table in the SplitDialog
private void enterSplitRowDetails(DialogFixture splitDialog, int cert, int distFrom, int distTo) {
    splitDialog.button("btnAdd").click();
    int row = splitDialog.table().rowCount();
    splitDialog.table().enterValue(row(row - 1).column(0), String.valueOf(cert));
    splitDialog.table().enterValue(row(row - 1).column(1), String.valueOf(distFrom));
    splitDialog.table().enterValue(row(row - 1).column(2), String.valueOf(distTo));
}

【问题讨论】:

  • FEST 可能会放大您的应用程序中的潜在问题; profile 确定。
  • 除了不断尝试寻找原因之外别无他法......切换到另一个测试框架根本没有帮助(即使它看起来可能会意外工作;-)在你的鞋子里,我会从两端尝试:替换整个应用程序中的部分(用普通的 J/X/Tree、JXTable 等替换树表)并尝试使用 SSCCE 来测试较小部分的交互。
  • 感谢克娄巴特拉。我已经编辑了这个问题。这只能通过节日发生。当我手动进行测试时不会发生这种情况。
  • 手动?你能提供更多细节吗?
  • 我有一个类似的问题 - 第一个测试运行正常,但下一个测试在 gui 事件之间运行时有中断(超过 10 秒)。看起来测试 gui 事件正在等待某些东西(并发问题?)我也从 jdk 1.6 迁移到 1.7。

标签: java swing swingx fest jxtreetable


【解决方案1】:

Emm...这是一个很有趣的问题;

我想这个问题包含的真正需要的细节较少尤其是机器人集成和 IO 解决方案细节所以我不能只给你一个正确的答案......

无论如何,我会尝试用我的方式稍微分析一下声音中的问题......

首先。根据您的屏幕截图 cmets,我可以注意到所有 “30 秒左右的暂停” 都发生在某些情况下,据我所知,流读取过程 “选择/搜索” (您的应用获取一些数据输出等)。所以也许它比你想象的要深得多,因为它可能是线程问题;

我在您的代码 sn-ps 中找不到 GuiQuery/GuiTask/GuiActionRunne 类的用法,所以我可能建议在上述情况下可能会发生“同步问题”...

第二。好的...如果仍然是线程问题,我可能会建议机器人和 IO 解决方案都在某个线程(主线程或其他东西)中,因为根据您的提示 “有时 0.39s 到 0.40 a执行一组操作。这些操作在 JXTreeTable 中有一行时完成。" ... GUI 正在等待某个进程完成...

第三。 又一次......根据this issue作为

"建议开启自动检查以验证所有 Swing 组件更新在 Swing 的 EDT(事件调度程序 线)。对于那些不熟悉 EDT 的人,它负责 在单独的线程中处理和更新所有 Swing 小部件,导致 应用程序永远不会失去对用户手势的响应能力(只是 简而言之,这里有更多关于 EDT 的信息)。为此,我们添加以下内容 挂钩测试:"

import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
import org.junit.BeforeClass;
...
    @BeforeClass
    public static void setUpOnce() {
        FailOnThreadViolationRepaintManager.install();
    }

下一步是启动框架或对话框。由于 JUnit 以自己的方式运行 线程,我们必须通过 Fest 启动框架或对话框,以确保, 再次,正确使用 EDT:

import org.fest.swing.edt.GuiActionRunner;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.fixture.FrameFixture;
import org.junit.Before;
... 
    private FrameFixture testFrame; 
    private AllTypesFrame  frame;
... 
    @Before 
    public void setUp()  { 
        frame =  GuiActionRunner.execute(new GuiQuery<AllTypesFrame>() { 
            protected AllTypesFrame executeInEDT() { 
                return new AllTypesFrame(); 
            }
        }); 
        testFrame = new FrameFixture(frame); 
        testFrame.show(); 
    }

... 让我觉得这可能是 First 和 Second 提示中描述的 “线程问题”。 .

所以,作为一个结论,我可以说也许你必须多线程测试一下,因为这显然是某种同步问题......

附: @sethu,在你开始调试之前,我想指出一点...... 我仍然怀疑这里发生了线程冲突(请参阅我之前的提示),因为我可能会注意到,您的代码 sn-ps 显示静态表达式使用来调用 Pause.pause(...) 或 FinderUtilities.getDialogWithTitle(...) 等方法我看不到整个项目架构,因此很难根据表示的位进行分析,但很明显 “手动测试” 运行良好,因为动作侦听器会实时做出反应,但 fest 测试 会令人讨厌延迟,因为它使用一些“计时器”来倒计时,直到发生点击模拟等,当然它是一个需要单独线程的后台进程......仔细观察调试可能在你的代码 UI 线程和 fest 线程的某个地方发生冲突(参见静态方法,thread.sleep 等)fest 线程可能阻塞(覆盖)UI 的点...:S 顺便问一下Pause.pause(...) 的方法是什么?

附言 如果您有其他信息,请评论我的回答


如果我的回答对你有帮助,请报告

【讨论】:

  • 感谢您的回答。我确实安装了 FailOnThreadViolationRepaintManager。我所做的一切都不是在 EDT 上。事实上,我使用的是我开发的一个名为 SwingObjects 的框架——它在这里:github.com/Sethuraman/swingobjects.git。该框架本质上不允许您违反 EDT。对于第一个提示 - 我没有做任何流式阅读。一切都只在 GUI 上进行。一切都发生在 EDT 上。没有数据库的东西或任何东西。对于第二个和第三个......因为 EDT 只有一个线程,不确定它们是否适用。
  • 另外,我可以手动处理这个。 (当我自己执行测试时,而不是让 fest 这样做)。我没有遇到任何延误。没有屏幕挂起或任何东西。有问题。我认为主要是与巨星。它也可能是我的代码。我不确定它是什么
  • “(当我自己执行测试时,而不是让 fest 执行此操作)。我没有遇到任何延迟。” ...如果只有 fest 集成会延迟某些东西(线程作为常见的东西),那让我认为机器人集成(它的线程)会阻塞 UI 线程,但很难说实际发生在哪里:SI 假设可能发生线程冲突静态块中的某个地方(通常)(Thread.sleep(int) 等);...
  • ...无论如何,很难分析有较少的细节。所以我必须问...您是否遵循此处 fest.codehaus.org/Accessing+Swing+Components+in+the+EDT 和此处 fest.codehaus.org/ 所述的所有这些集成规则“GuiQuery/GuiTask/GuiActionRunne”写作+EDT安全+GUI+测试?我之所以问,是因为我观察了代码 sn-ps(您的问题包含)并且找不到三个类用法的任何色调:S
【解决方案2】:

我不知道您的机器人设置是什么,但您至少可以尝试为您使用的机器人设置 idleTimeout 和其他超时。默认超时为 10 秒(查看 org.fest.swing.core.Settings)。在我减少它之后(前 1000 毫秒,接下来的 100 毫秒),我注意到机器人工作得更快。

robot().settings().idleTimeout(YOUR_TIMEOUT)

这是我的测试设置和一种测试方法。希望是明确的。 这里有我的之前/之后

private static int testMethodCounter = 0;
private static EmergencyAbortListener mEmergencyAbortListener;
private FrameFixture workbenchFrame;
private Robot robot2;
private static final int myIdleTimeout = 100;

@Before
public void setUp() throws Exception {
    // my workaround to be able to start the app once and reuse for all tests 
    if (testMethodCounter == 0) {
        robot2 = BasicRobot.robotWithNewAwtHierarchy();
        GuiActionRunner.execute(new GuiTask() {
            @Override
            protected void executeInEDT() throws Throwable {
                 ApplicationLauncher.application(ProgramRun.class).start();
            }
        });
    } else {
        // the second test method see all before created gui components
        robot2 = BasicRobot.robotWithCurrentAwtHierarchy();
    }
    testMethodCounter++;
    robot2.settings().idleTimeout(myIdleTimeout);

    workbenchFrame = WindowFinder.findFrame(FrameNames.WORKBENCH.getName()).withTimeout(10000)
            .using(robot2);
}

@After
public void tearDown() {
    // current window will not be closed
    robot2.cleanUpWithoutDisposingWindows();

}

@Test
public void someSmokeTest() throws Exception {

    Pause.pause(1000);
    // perform some test specific gui actions

    // here is very important moment, I need new robot because 
    // workbenchFrame.button(ButtonNames.SOME_BUTTON_NAME).click(); creates new dialog
    // which will be avilable in AWT stack after creation

    robot2.cleanUpWithoutDisposingWindows();
    robot2 = BasicRobot.robotWithCurrentAwtHierarchy();
    // the new Robot needs timeout setup
    // without this I have long breaks between gui events
    robot2.settings().idleTimeout(myIdleTimeout);

    workbenchFrame.button(ButtonNames.SOME_BUTTON_NAME).click();

    DialogFixture dialog = WindowFinder.findDialog("dialog2") 
              .withTimeout(5000).using(robot2);

    // some actions on the dialog        


    // once again next new dialog
    workbenchFrame.menuItem(MenuItemNames.NAME).click();

    robot2.cleanUpWithoutDisposingWindows();
    robot2 = BasicRobot.robotWithCurrentAwtHierarchy();
    // and idleTimeout setup once again, new Robot needs new setup
    robot2.settings().idleTimeout(myIdleTimeout);

    // next actions + assertion
}

【讨论】:

  • 更改机器人设置没有帮助..不幸的是..感谢您的回答..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-12
  • 1970-01-01
相关资源
最近更新 更多