4

I am not sure how to explain this. But I'll try.. Fest slows down to crawl while working with JXTreeTable of swingx. It doesn't slow down initially. It works fine for a while, but after a while when the same actions are repeated it slows down badly.

I have raised a bug for this in github. Please tell me if this is something that I am doing wrong instead. I am not able to reproduce the problem when I tried to create an SSCCE.

Anyway, here's a video of it slowing down.

http://screencast.com/t/liNttCw2In0w

At times 0.39s to 0.40 a set of operations are performed. These are done when there is one row in the JXTreeTable.

At time 0.49 to end of recording the same operation is repeated but there are now 3 rows in the table, it takes very long for the mouse to click.

I have attached a screenshot taken at the time when fest slows down, which attempts to explain it more

enter image description here

This is the code that does the work:

Step 1) Selecting a node from the tree is done as below:

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

Step 2) Searching and selecting a row from the 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");
}

Step 3) Showing the pop up menu and clicking the row

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

I am not sure why its slowing down. Please let me know if there is any more info I can help with. Would be grateful for some help in solving the problem

I have profiled the application and I dont find anything untoward happening. I dont have a lot of experience profiling applications. I would be grateful if someone could have a second look at this. I profiled it with yourkit and have uploaded the snapshot dump here:

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

Any help will be greatly appreciated..

EDIT:

I think I forgot to mention the same thing works when I do it manually. It only slows down with fest. That leads me to believe that there is an issue with fest maybe?

Sorry about that.

EDIT 2: As request by Marcin (sorry for the delay Marcin).. Here's the code when the first row is getting split

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));
}
sethu
  • 8,181
  • 7
  • 39
  • 65
  • FEST may be amplifying a latent problem in your application; [profile](http://stackoverflow.com/q/2064427/230513) to be sure. – trashgod Jun 14 '13 at 09:10
  • there is no way except keep trying to find the reason ... switching to another testing framework wouldn't help at all (even if it might seem to work accidentally ;-) In your shoes, I would try from both ends: replace parts in your full application (f.i. the treetable with a plain J/X/Tree, JXTable, ...) and trying SSCCEs for testing the interaction of smaller parts. – kleopatra Jun 17 '13 at 07:47
  • thanks kleopatra. I have edited the question. This only happens through fest. It doesn't happen when I do the testing manually. – sethu Jun 17 '13 at 07:55
  • manually? could you give more details? – user592704 Jun 19 '13 at 18:33
  • I have a similar problem - be me the first test runs normal but next tests run with breaks (more than 10sec) between gui events. It looks like the test gui events waits for something (concurrency problem?) I have also migrated from jdk 1.6 to 1.7. – Marcin Sanecki Jun 20 '13 at 07:54
  • @MarcinSanecki can you show an SSCCE that demonstrates the behaviour? – kleopatra Jun 20 '13 at 10:18
  • @kleopatra I have edited my answer - SSCCE is there. – Marcin Sanecki Jun 20 '13 at 10:58
  • @user592704 by manually I mean, if I move the mouse and type on the keyboard and execute my test case myself then the application responds properly. It's only when I use fest to move the mouse it slows down. I hope that was clear.. – sethu Jun 20 '13 at 23:49
  • @sethu Have you tried to change the idleTimeout? How many times do you set new robot in your tests? – Marcin Sanecki Jun 21 '13 at 05:51
  • @sethu could you put the code from your test where the first row is splited, so that it will be possible to see what is done in the test? – Marcin Sanecki Jun 22 '13 at 20:19
  • @sethu how it is going? did you succeed to solve the "pauses problem"? – user592704 Jun 26 '13 at 16:36
  • Nope. Think thats stuck in limbo. I have ignored the test for now and proceeding with my development. Will need to return back to it when time permits. The only think I havent done is debug fest's code. THink I'll start cracking on that soon. – sethu Jun 26 '13 at 16:49
  • @sethu see my answer updates please – user592704 Jun 28 '13 at 19:53

2 Answers2

1

Emm... It is quite interesting question;

I suppose the question contains less really required details especially the robot integration and IO solutions details so I cannot just give you a proper answer...

Anyway, I'll try to analyze the problem in voice a little bit in my way...

First. According to your screenshot comments, I can notice that all "30s pauses or so" occur on some, as I can get it, stream reading process "select/search" (your app gets some data to output etc). So maybe it is much deeper than you think because it is probably thread problem;

I couldn't find the GuiQuery/GuiTask/GuiActionRunne classes usage in your code snippets so I may suggest the "synch problem" may take place in the mentioned case...

Second. OK... If it is still the thread problem I may suggest the robot and IO solutions are both in some ONE thread (the Main thread or something) because, according to your tips as "At times 0.39s to 0.40 a set of operations are performed. These are done when there is one row in the JXTreeTable." ... GUI is waiting for some process to be completed...

Third. And again... According to this issue as

"It is recommended to turn on an automated check to verify that all Swing components updates are done in Swing’s EDT (Event Dispatcher Thread). For those unfamiliar with the EDT, it is responsible for handling and updating all Swing widgets in a separate thread, causing that the application never loses responsiveness to user gestures (just in short, more about the EDT here). To do that, we add the following hook to the test:"

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

Next step is to launch the frame or dialog. As JUnit runs in its own thread, we must launch the frame or dialog through Fest, to ensure, again, that EDT is properly used:

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

... makes me think it is maybe the "thread-problem" which is described in the First and Second tips...

so, as a conclusion, I can say that maybe you have to multi-thread your test a little more because it is obviously some kind of synch problem...

P.S. @sethu, before you start your debugging I want to point a little... I still suspect threads conflict is taking place here (see my previous tips) because, as I may notice, your code snippets are showing static expressions usage to invoke methods like Pause.pause(...) or FinderUtilities.getDialogWithTitle(...) etc I cannot see the whole project architecture so it is hard to analyze according the represented bits but it is pretty clear the "manual testing" goes fine because action listeners react in real time but fest testing does the annoying delays because it uses some "timer" to countdown until a click emulation occurs etc and of course it is a background process which needs a separate thread... Watch debugging carefully maybe somewhere in your code UI thread and fest thread do conflict (see static methods, thread.sleep etc) the points where fest thread could block (override) the UI's one... :S By the way what method Pause.pause(...) does?

P.P.S. If you have some additional information please comment my answer


Report if my answer helps you

user592704
  • 3,674
  • 11
  • 70
  • 107
  • thanks for the answer.. I do have the FailOnThreadViolationRepaintManager installed. There is nothing that I am doing thats not on the EDT. In fact I work with a framework I have developed called SwingObjects - its here: https://github.com/Sethuraman/swingobjects.git. The framework inherently doesn't allow you to violate EDT. For the first tip - I am not doing any stream reading. Everything revolves just on the GUI. Everything happens on the EDT. No database stuff or anything.For the second and third.. since there is only one thread the EDT, not sure if they apply. – sethu Jun 23 '13 at 15:35
  • Also, I am able to work on this manually. (when I execute the test myself, rather than having fest do this). I experience no delays. No screen hanging or anything. There is something wrong. I think mostly with fest. It could also be my code. I am not sure what it is – sethu Jun 23 '13 at 15:37
  • "(when I execute the test myself, rather than having fest do this). I experience no delays." ... if only fest integration delays something (a thread as a common stuff), that makes me think that robot integration (its thread) blocks UI thread but it is hard to say where that actually happens :S I suppose the threads conflict may occur someplace (as a rule) in a static block (the Thread.sleep(int) etc);... – user592704 Jun 24 '13 at 00:22
  • ...anyway, it is hard to analyze having less details. So I must ask... did you follow all these integration rules the "GuiQuery/GuiTask/GuiActionRunne" as described here fest.codehaus.org/Accessing+Swing+Components+in+the+EDT and here fest.codehaus.org/Writing+EDT-safe+GUI+tests? I am asking because I observed the code snippets (your question contains) and couldn't find any tint of the three classes usage :S – user592704 Jun 26 '13 at 16:39
0

I do not know what are your robot settings but you can at least try to set idleTimeout and other timeouts for the robot you use. The default timeout is 10 sec (look in org.fest.swing.core.Settings). After I decrease it (first 1000ms, next 100ms) I noticed that robot works faster.

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

Here is my test setup and one test method. Hope is clear. Here you have my before/after

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
}
Marcin Sanecki
  • 1,324
  • 3
  • 19
  • 35
  • Changing the robot setting does not help.. unfortunately.. thanks for the answer though.. – sethu Jun 23 '13 at 15:38