4

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

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

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

http://screencast.com/t/liNttCw2In0w

在 0.39s 到 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));
}
4

2 回答 2

1

嗯……这是一个很有趣的问题;

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

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

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

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

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

第三。再一次......根据这个问题

“建议开启自动检查,验证所有 Swing 组件更新都在 Swing 的 EDT(Event Dispatcher Thread)中完成。对于不熟悉 EDT 的人,它负责在单独的线程中处理和更新所有 Swing 小部件, “

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(); 
    }

...让我觉得这可能是第一个第二个技巧中描述的“线程问题” ...

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

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

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


报告我的回答是否对您有帮助

于 2013-06-23T05:23:35.547 回答
0

我不知道您的机器人设置是什么,但您至少可以尝试为您使用的机器人设置 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
}
于 2013-06-20T09:18:31.423 回答