swingx-master/swingx-action/src/test/java/org/jdesktop/swingx/action/AbstractActionExtTest.java
/*
 * Created on 27.01.2006
 *
 */
package org.jdesktop.swingx.action;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.awt.event.ActionEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Logger;

import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JRadioButton;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JToggleButton;

import org.jdesktop.test.EDTRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(EDTRunner.class)
@SuppressWarnings("nls")
public class AbstractActionExtTest {
    @SuppressWarnings("all")
    private static final Logger LOG = Logger.getLogger(AbstractActionExtTest.class.getName());
    
    protected ActionContainerFactory factory;
    
    @Before
    public void setUp() throws Exception {
        factory = new ActionContainerFactory(null);
    }

    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * 
     * test that configured button is kept in synch with
     *  action selected state and the other way round. This uses a 
     *  custom configure via the ActionContainerFactory.configureSelectableButton.
     * The direction from button to action is broken.
     */
    @Test
    public void testButtonSelectedSynchAction() {
        AbstractActionExt extAction = createStateAction();
        boolean actionSelected = true;
        extAction.setSelected(actionSelected);
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        // invert action selected and assert that the change is taken up
        // by the button
        extAction.setSelected(!actionSelected);
        assertEquals("button selected must be synched to action", 
                !actionSelected, button.isSelected());
        // reset button 
        button.setSelected(actionSelected);
        // sanity: the button did take the direct selection change
        assertEquals(actionSelected, button.isSelected());
        // assert that changed selected is taken up by action
        assertEquals("action selected must be synched to button", 
                actionSelected, extAction.isSelected());
    }


    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * 
     * test that configured button is kept in synch with
     *  action selected state and the other way round. This uses the 
     *  ActionContainerFactory.createButton().
     * The direction from button to action is broken.
     */
    @Test
    public void testCreateButtonSelectedSynchAction() {
        AbstractActionExt extAction = createStateAction();
        boolean actionSelected = true;
        extAction.setSelected(actionSelected);
        JToggleButton button = (JToggleButton) factory.createButton(extAction);
        // invert action selected and assert that the change is taken up
        // by the button
        extAction.setSelected(!actionSelected);
        assertEquals("button selected must be synched to action", 
                !actionSelected, button.isSelected());
        // reset button 
        button.setSelected(actionSelected);
        // sanity: the button did take the direct selection change
        assertEquals(actionSelected, button.isSelected());
        // assert that changed selected is taken up by action
        assertEquals("action selected must be synched to button", 
                actionSelected, extAction.isSelected());
    }



    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     *  
     * test that the button always has mostly one
     * Action registered as itemListener and that
     * this registered listener is the same as the buttons
     * action.
     *
     */
    @Test
    public void testButtonOneActionAsItemListener() {
        AbstractActionExt extAction = createStateAction();
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        assertCountAsItemListener(button, extAction, 1);
        factory.configureSelectableButton(button, null, null);
        // assert that the previous action is removed as itemListener
        assertCountAsItemListener(button, extAction, 0);
    }
    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * test that configured button is kept in synch with
     *  maximal one action's selected state
     */
    @Test
    public void testButtonSelectedNullAction() {
        AbstractActionExt extAction = createStateAction();
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        // we are sure that the button selected is true (has dedicated test)
        // now configure it with a different action, unselected
        AbstractActionExt extActionB = createStateAction();
        factory.configureSelectableButton(button, extActionB, null);
        // invert the old action selected and assert that the change 
        // does not effect the taken up by the button
        extAction.setSelected(!extAction.isSelected());
        assertEquals("button selected must be uneffected by old action",
                extActionB.isSelected(), button.isSelected());
    }

    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * test that PCLs related to a previous button are 
     * unregistered from the Action after release.
     *
     */
    @Test
    public void testButtonReleaseActionReleasePCL() {
        AbstractActionExt extAction = createStateAction();
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        // sanity: expect it to be 2 - one is the menuitem itself, another 
        // the TogglePCL registered by the ActionContainerFacory
        assertEquals(2, extAction.getPropertyChangeListeners().length);
        // set the button's action to null
        factory.configureSelectableButton(button, null, null);
        // assert that button related PCLs are removed from the action's listener list
        assertEquals(0, extAction.getPropertyChangeListeners().length);
    }

    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * test that configured button is no longer kept in
     * synch after setting the action to null.
     */
    @Test
    public void testButtonSelectedReleasedSynchAction() {
        AbstractActionExt extAction = createStateAction();
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        // now we unconfigure it with a null action
        factory.configureSelectableButton(button, null, null);
        // invert the old action selected and assert that the change 
        // does not effect the taken up by the button
        boolean oldSelected = button.isSelected();
        extAction.setSelected(!extAction.isSelected());
        assertEquals("button selected must be uneffected by old action",
                oldSelected, button.isSelected());
    }

    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * test that configured button is kept in synch with
     *  maximal one action's selected state
     */
    @Test
    public void testButtonSelectedMaxOneSynchAction() {
        AbstractActionExt extAction = createStateAction();
        boolean actionSelected = true;
        extAction.setSelected(actionSelected);
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        // we are sure that the button selected is true (has dedicated test)
        // now configure it with a different action, unselected
        AbstractActionExt extActionB = createStateAction();
        factory.configureSelectableButton(button, extActionB, null);
        // sanity: the new action is not effected by the old
        // currently this may accidentally pass because the back direction isn't
        // synched!! 
        assertFalse(extActionB.isSelected());
        assertEquals("button selected must be initialized to new action",
                extActionB.isSelected(), button.isSelected());
        // invert the old action selected and assert that the change 
        // does not effect the taken up by the button
        extAction.setSelected(!actionSelected);
        // need to be done twice, the first toggle produces 
        extAction.setSelected(actionSelected);
        assertEquals("button selected must be uneffected by old action",
                extActionB.isSelected(), button.isSelected());
    }

    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * test that button is configured with initial action selected state.
     *
     */
    @Test
    public void testButtonSelectedInitialSynchAction() {
        AbstractActionExt extAction = createStateAction();
        boolean actionSelected = true;
        extAction.setSelected(actionSelected);
        JToggleButton button = new JToggleButton();
        boolean buttonSelected = button.isSelected();
        // sanity: different selected state
        assertTrue(actionSelected != buttonSelected);
        factory.configureSelectableButton(button, extAction, null);
        assertEquals("action selection must be unchanged", actionSelected, extAction.isSelected());
        assertEquals("button selected must be initialized", actionSelected, button.isSelected());
    }

    /**
     * test method contract: configureSelectable must throw runtime exception
     * for non-state action.
     *
     */
    @Test
    public void testExceptionOnNonStateAction() {
        AbstractActionExt actionExt = createStateAction();
        actionExt.setStateAction(false);
        JToggleButton button = new JToggleButton();
        try {
           factory.configureSelectableButton(button, actionExt, null);
           fail("configureSelectable didn't throw IllegalArgument for non-state action ");
        } catch (IllegalArgumentException e) {
            // nothing todo - this is what we expect
        } catch (Exception e) {
            fail("caught unexpected exception " + e);
        }
        
    }

    /**
     * Issue #229-swingx: increasing listener list in column actions.
     * 
     * sub-issue: ActionContainerFactory doesn't check if the action is
     * already synchronizing to the same button. 
     */
    @Test
    public void testToggleButtonConfigure() {
        // this should pass after giving the gc "reasonable" chance to
        // have collected the unreachable...
//        assertToggleButtonConfigure(new JToggleButton(), new JToggleButton());

    }

    /**
     * Issue #229-swingx: increasing listener list in column actions.
     * 
     * sub-issue: ActionContainerFactory doesn't check if the action is
     * already synchronizing to the same button. 
     */
    @Test
    public void testToggleButtonConfigureToggleWithSame() {
        assertToggleButtonConfigureWithSame(new JToggleButton());
        assertToggleButtonConfigureWithSame(new JRadioButton());
        assertToggleButtonConfigureWithSame(new JCheckBox());
        assertToggleButtonConfigureWithSame(new JRadioButtonMenuItem());
        assertToggleButtonConfigureWithSame(new JCheckBoxMenuItem());

    }
    
    private void assertToggleButtonConfigureWithSame(AbstractButton button) {
        AbstractActionExt extAction = createStateAction();
        assertEquals(0, extAction.getPropertyChangeListeners().length);
        factory.configureSelectableButton(button, extAction, null);
        // sanity: expect it to be 2 - one is the menuitem itself, another 
        // the TogglePCL registered by the ActionContainerFacory
        
        assertEquals(2, extAction.getPropertyChangeListeners().length);
        factory.configureSelectableButton(button, extAction, null);
        // JW: wrong assumption!! Valid only if first == second, for
        // different buttons we actually expect the listener count to be increased!
        // Note to myself: remove this comment after correcting the method call
        // sequence here in the test ...
        assertEquals(2, extAction.getPropertyChangeListeners().length);
        
    }

    /**
     * Issue #229-swingx: increasing listener list in column actions.
     * 
     * sub-issue: ActionContainerFactory registers the action multiple times to
     * the same button as ItemListener
     */
    @Test
    public void testToggleButtonAddItemListenerToSame() {
        assertAddItemListenerToSame(new JToggleButton());
        assertAddItemListenerToSame(new JRadioButton());
        assertAddItemListenerToSame(new JCheckBox());
        assertAddItemListenerToSame(new JRadioButtonMenuItem());
        assertAddItemListenerToSame(new JCheckBoxMenuItem());
    }
    
    private void assertAddItemListenerToSame(AbstractButton checkBoxItem) {
        AbstractActionExt extAction = createStateAction();
        factory.configureSelectableButton(checkBoxItem, extAction, null);
        assertCountAsItemListener(checkBoxItem, extAction, 1 );
        factory.configureSelectableButton(checkBoxItem, extAction, null);
        assertCountAsItemListener(checkBoxItem, extAction, 1 );
      
    }

    protected AbstractActionExt createStateAction() {
        AbstractActionExt extAction = new AbstractActionExt("dummy") {

            public void actionPerformed(ActionEvent e) {
            }
            
        };
        extAction.setStateAction();
        return extAction;
    }

    /**
     * assert that the given itemListener is registered exactly
     * expectedCount times to the given button.
     * @param checkBoxItem
     * @param extAction
     * @param expectedCount
     */
    protected void assertCountAsItemListener(AbstractButton checkBoxItem, ItemListener extAction, int expectedCount) {
        int count = 0;
        ItemListener[] itemListeners = checkBoxItem.getItemListeners();
        for (int j = 0; j < itemListeners.length; j++) {
            if (extAction == itemListeners[j]) {
                count++;
            }
        }
        assertEquals("ItemListener registration count", expectedCount, count);
        
    }

    /**
     * Issue #4-swinglabs: infinite loop when setting long destricption.
     *
     */
    @Test
    public void testLongDescriptionLoop() {
        AbstractActionExt action = createStateAction();
        action.setLongDescription("some");
    }

    /**
     * core issue: 
     * set enabled via putValue leads to inconsistent state.
     * fixed in jdk6
     */
    @Test
    public void testFireEnabled() {
        Action action = new AbstractAction("dummy") {

            public void actionPerformed(ActionEvent e) {
                // nothing to do
                
            }
            
        };
        PropertyChangeListener l = new PropertyChangeListener() {

            public void propertyChange(PropertyChangeEvent evt) {
                if ("enabled".equals(evt.getPropertyName())) {
                    assertEquals(evt.getNewValue(), ((Action) evt.getSource()).isEnabled());
                }
                
            }
            
        };
        action.addPropertyChangeListener(l);
        action.putValue("enabled", false);
        
    }
}
