Skip to content

Commit

Permalink
[PDI-14411] bad index after closing first tab
Browse files Browse the repository at this point in the history
fix, tests, changed TabSet/TabItem for headless testing
  • Loading branch information
tgf committed Nov 10, 2015
1 parent e833a3d commit 8459a79
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 4 deletions.
7 changes: 5 additions & 2 deletions ui/src/org/pentaho/xul/swt/tab/TabItem.java
Expand Up @@ -2,7 +2,7 @@
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com
* Copyright (C) 2002-2015 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
Expand Down Expand Up @@ -45,7 +45,7 @@ public TabItem( TabSet tabset, String text, String id ) {
super();
this.tabset = tabset;
this.id = id;
item = new CTabItem( tabset.getSwtTabset(), SWT.CLOSE );
item = createTabItem( tabset );
setText( text );
tabset.addTab( this );
}
Expand Down Expand Up @@ -151,4 +151,7 @@ public boolean isChanged() {
return changed;
}

protected CTabItem createTabItem( TabSet tabset ) {
return new CTabItem( tabset.getSwtTabset(), SWT.CLOSE );
}
}
14 changes: 12 additions & 2 deletions ui/src/org/pentaho/xul/swt/tab/TabSet.java
Expand Up @@ -2,7 +2,7 @@
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com
* Copyright (C) 2002-2015 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
Expand Down Expand Up @@ -48,7 +48,7 @@ public class TabSet implements SelectionListener, CTabFolder2Listener {

public TabSet( Composite parent ) {
super();
tabfolder = new CTabFolder( parent, SWT.MULTI );
tabfolder = createTabFolder( parent );

tabfolder.setSimple( false );
tabfolder.setUnselectedImageVisible( true );
Expand Down Expand Up @@ -104,6 +104,11 @@ public void notifySelectListeners( TabItem item ) {
addItemToHistory( item );
}

protected CTabFolder createTabFolder( Composite parent ) {
return new CTabFolder( parent, SWT.MULTI );
}


/**
* Add a tab item to the tab usage history
*
Expand Down Expand Up @@ -207,6 +212,11 @@ public void setSelected( TabItem item ) {
}

public void remove( TabItem item ) {
int itemIndex = tabList.indexOf( item );
if ( itemIndex >= 0 && itemIndex < selectedIndex ) {
// removal would change selected
selectedIndex--;
}
tabList.remove( item );
item.dispose();
}
Expand Down
156 changes: 156 additions & 0 deletions ui/test-src/org/pentaho/xul/swt/tab/TabSetTest.java
@@ -0,0 +1,156 @@
/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2015 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/

package org.pentaho.xul.swt.tab;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class TabSetTest {

/**
* PDI-14411 NPE on Ctrl-W
*/
@Test
public void testCloseFirstTabOfTwo() {
final CTabFolder cTabFolder = mock( CTabFolder.class );
final TabSet tabSet = createTabSet( cTabFolder );

final CTabItem cTabItem1 = mock( CTabItem.class );
TabItem firstItem = createItem( tabSet, "first", "1st", cTabItem1 );
final CTabItem cTabItem2 = mock( CTabItem.class );
TabItem secondItem = createItem( tabSet, "second", "2nd", cTabItem2 );

assertEquals( 0, tabSet.indexOf( firstItem ) );
assertEquals( 1, tabSet.indexOf( secondItem ) );
tabSet.setSelected( firstItem );
assertEquals( 0, tabSet.getSelectedIndex() );

wireDisposalSelection( cTabFolder, tabSet, cTabItem1, cTabItem2 );

firstItem.dispose();
assertEquals( -1, tabSet.indexOf( firstItem ) );
assertNotNull( "selected is null", tabSet.getSelected() );
}

/**
* Ctrl-W on first and second in succession would close first and third
*/
@Test
public void testCloseFirstTabOfThree() {
final CTabFolder cTabFolder = mock( CTabFolder.class );
final TabSet tabSet = createTabSet( cTabFolder );

final CTabItem cTabItem1 = mock( CTabItem.class );
TabItem firstItem = createItem( tabSet, "first", "1st", cTabItem1 );
final CTabItem cTabItem2 = mock( CTabItem.class );
TabItem secondItem = createItem( tabSet, "second", "2nd", cTabItem2 );
TabItem thirdItem = createItem( tabSet, "third", "3rd", mock( CTabItem.class ) );

assertEquals( 0, tabSet.indexOf( firstItem ) );
assertEquals( 1, tabSet.indexOf( secondItem ) );
assertEquals( 2, tabSet.indexOf( thirdItem ) );

wireDisposalSelection( cTabFolder, tabSet, cTabItem1, cTabItem2 );
tabSet.setSelected( firstItem );
assertEquals( 0, tabSet.getSelectedIndex() );

firstItem.dispose();
assertEquals( "should select second", secondItem, tabSet.getSelected() );
}

@Test
public void testRegularCloseTab() {
final CTabFolder cTabFolder = mock( CTabFolder.class );
final TabSet tabSet = createTabSet( cTabFolder );

final CTabItem cTabItem1 = mock( CTabItem.class );
TabItem firstItem = createItem( tabSet, "first", "1st", cTabItem1 );
final CTabItem cTabItem2 = mock( CTabItem.class );
TabItem secondItem = createItem( tabSet, "second", "2nd", cTabItem2 );
TabItem thirdItem = createItem( tabSet, "third", "3rd", mock( CTabItem.class ) );

assertEquals( 0, tabSet.indexOf( firstItem ) );
assertEquals( 1, tabSet.indexOf( secondItem ) );
assertEquals( 2, tabSet.indexOf( thirdItem ) );

// after close the previous tab is selected if available
wireDisposalSelection( cTabFolder, tabSet, cTabItem2, cTabItem1 );
tabSet.setSelected( secondItem );
assertEquals( 1, tabSet.getSelectedIndex() );
secondItem.dispose();
assertEquals( "should select first", firstItem, tabSet.getSelected() );
}

private TabSet createTabSet( final CTabFolder cTabFolder ) {
return new TabSet( null ) {
@Override
protected CTabFolder createTabFolder( Composite parent ) {
return cTabFolder;
}
};
}

private TabItem createItem( TabSet tabSet, String name, String id, final CTabItem cTabItem ) {
return new TabItem( tabSet, name, id ) {
@Override
protected CTabItem createTabItem( TabSet tabset ) {
return cTabItem;
}
};
}

protected void wireDisposalSelection(
final CTabFolder cTabFolder,
final TabSet tabSet,
final CTabItem closedItem,
final CTabItem nextSelectItem ) {
// emulate swt side
// on CTabItem disposal CTabFolder selects another item and notifies TabSet
final Boolean[] disposed = { false };
when( closedItem.isDisposed() ).then( new Answer<Boolean>() {
public Boolean answer( InvocationOnMock invocation ) throws Throwable {
return disposed[0];
}
} );
doAnswer( new Answer<Void>() {
public Void answer( InvocationOnMock invocation ) throws Throwable {
Event evt = new Event();
evt.item = nextSelectItem;
evt.widget = cTabFolder;
tabSet.widgetSelected( new SelectionEvent( evt ) );
disposed[0] = true;
return null;
}
} ).when( closedItem ).dispose();
}

}

0 comments on commit 8459a79

Please sign in to comment.