|
What this is
Other links
The source code/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.util.actions; import org.netbeans.junit.*; import junit.textui.TestRunner; import org.openide.nodes.Node; import org.openide.util.HelpCtx; import java.util.List; import java.util.ArrayList; import java.util.Arrays; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import java.awt.event.ActionEvent; import java.util.Collection; import java.util.Collections; import org.openide.util.lookup.InstanceContent; /** Test that node actions are enabled on the right nodes and track selection changes. * @author Jesse Glick */ public class NodeActionTest extends NbTestCase { static { // Get Lookup right to begin with. ActionsInfraHid.class.getName(); } public NodeActionTest(String name) { super(name); } public static void main(String[] args) { TestRunner.run(new NbTestSuite(NodeActionTest.class)); } private Node n1, n2, n3; /** * in order to run in awt event queue * fix for #39789 */ protected boolean runInEQ() { return true; } protected void setUp() throws Exception { n1 = new AbstractNode(Children.LEAF); n1.setName("n1"); n1.setDisplayName("text"); n2 = new AbstractNode(Children.LEAF); n2.setName("n2"); n2.setDisplayName("text"); n3 = new AbstractNode(Children.LEAF); n3.setName("n3"); n3.setDisplayName("somethingelse"); } public void testBasicUsage() throws Exception { SimpleNodeAction a1 = (SimpleNodeAction)SystemAction.get(SimpleNodeAction.class); ActionsInfraHid.WaitPCL l = new ActionsInfraHid.WaitPCL(NodeAction.PROP_ENABLED); try { // Check enablement logic. a1.addPropertyChangeListener(l); assertFalse(a1.isEnabled()); // Note that changes to enabled are made asynch, so it is necessary to listen // for that (will not generally take effect immediately). ActionsInfraHid.UT.setCurrentNodes(new Node[] {n1}); assertTrue(l.changed()); l.gotit = 0; assertTrue(a1.isEnabled()); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n1, n2}); assertTrue(l.changed()); l.gotit = 0; assertFalse(a1.isEnabled()); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n2}); assertTrue(l.changed()); l.gotit = 0; assertTrue(a1.isEnabled()); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n3}); assertTrue(l.changed()); l.gotit = 0; assertFalse(a1.isEnabled()); // Check that the action is performed correctly. ActionsInfraHid.UT.setCurrentNodes(new Node[] {n1}); assertTrue(l.changed()); l.gotit = 0; assertTrue(a1.isEnabled()); a1.actionPerformed(null); a1.actionPerformed(new ActionEvent(a1, ActionEvent.ACTION_PERFORMED, "runit")); assertEquals(Arrays.asList(new List[] { Collections.singletonList(n1), Collections.singletonList(n1), }), a1.runOn); // Also that idempotent node list changes do not harm anything, at least. ActionsInfraHid.UT.setCurrentNodes(new Node[] {n1}); // It need not fire a change event; if not, just wait a moment for it to recalc. if (!l.changed()) { //System.err.println("waiting a moment..."); Thread.sleep(1000); } l.gotit = 0; assertTrue(a1.isEnabled()); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n3}); assertTrue(l.changed()); l.gotit = 0; assertFalse(a1.isEnabled()); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n3}); if (!l.changed()) { //System.err.println("waiting a moment..."); Thread.sleep(1000); } l.gotit = 0; assertFalse(a1.isEnabled()); } finally { a1.removePropertyChangeListener(l); ActionsInfraHid.UT.setCurrentNodes(new Node[0]); ActionsInfraHid.UT.setCurrentNodes(null); a1.runOn.clear(); } } public void testPerformActionWithArgs() throws Exception { SimpleNodeAction a1 = (SimpleNodeAction)SystemAction.get(SimpleNodeAction.class); try { assertFalse(a1.isEnabled()); assertEquals(Collections.EMPTY_LIST, a1.runOn); a1.actionPerformed(new ActionEvent(n1, ActionEvent.ACTION_PERFORMED, "exec")); a1.actionPerformed(new ActionEvent(new Node[] {n1}, ActionEvent.ACTION_PERFORMED, "exec")); assertEquals(Arrays.asList(new List[] { Collections.singletonList(n1), Collections.singletonList(n1), }), a1.runOn); // XXX probably NodeAction.actionPerformed with Node or Node[] should // first check that the action is in fact enabled on those nodes, else // throw an IllegalArgumentException; in which case add a test to that effect here } finally { ActionsInfraHid.UT.setCurrentNodes(new Node[0]); ActionsInfraHid.UT.setCurrentNodes(null); a1.runOn.clear(); } } /** Test that surviveFocusChange really controls whether node actions are enabled or not. */ public void testFocusChange() throws Exception { helpTestFocusChange(); // XXX does not work: refuses to collect the node actions! // Yet similar code works in CallbackSystemActionTest. // Profiler shows that the references are held only from WeakReference's, // one of which is in the finalizer queue. ??? /* ActionsInfraHid.doGC(); assertEquals("Garbage collection removed all SimpleNodeAction's", 0, SimpleNodeAction.INSTANCES); helpTestFocusChange(); */ } private void helpTestFocusChange() throws Exception { SimpleNodeAction a1 = (SimpleNodeAction)SystemAction.get(SimpleNodeAction.class); DoesNotSurviveFocusChgAction a2 = (DoesNotSurviveFocusChgAction)SystemAction.get(DoesNotSurviveFocusChgAction.class); ActionsInfraHid.WaitPCL l1 = new ActionsInfraHid.WaitPCL(NodeAction.PROP_ENABLED); ActionsInfraHid.WaitPCL l2 = new ActionsInfraHid.WaitPCL(NodeAction.PROP_ENABLED); try { assertEquals(null, ActionsInfraHid.UT.getCurrentNodes()); assertEquals(Collections.EMPTY_LIST, Arrays.asList(ActionsInfraHid.UT.getActivatedNodes())); a1.addPropertyChangeListener(l1); a2.addPropertyChangeListener(l2); assertFalse(a1.isEnabled()); assertFalse(a2.isEnabled()); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n1}); assertTrue(l1.changed()); l1.gotit = 0; assertTrue(a1.isEnabled()); assertTrue(l2.changed()); l2.gotit = 0; assertTrue(a2.isEnabled()); ActionsInfraHid.UT.setCurrentNodes(null); assertTrue(l2.changed()); l2.gotit = 0; assertFalse(a2.isEnabled()); if (!l1.changed()) { Thread.sleep(1000); } l1.gotit = 0; assertTrue(a1.isEnabled()); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n2}); assertTrue(l2.changed()); l2.gotit = 0; assertTrue(a2.isEnabled()); if (!l1.changed()) { Thread.sleep(1000); } l1.gotit = 0; assertTrue(a1.isEnabled()); // another trick, sets n1 to enable everything and then // switches to Node[0] to disable everything ActionsInfraHid.UT.setCurrentNodes(new Node[] {n1}); assertTrue(l1.changed()); l1.gotit = 0; assertTrue(a1.isEnabled()); assertTrue(l2.changed()); l2.gotit = 0; assertTrue(a2.isEnabled()); ActionsInfraHid.UT.setCurrentNodes(new Node[0]); assertTrue (l2.changed()); l2.gotit = 0; assertFalse(a2.isEnabled()); l1.gotit = 0; assertFalse(a1.isEnabled()); } finally { a1.removePropertyChangeListener(l1); a2.removePropertyChangeListener(l2); ActionsInfraHid.UT.setCurrentNodes(new Node[0]); ActionsInfraHid.UT.setCurrentNodes(null); } a1 = null; a2 = null; } /** Make sure NodeAction itself does not do anything dumb by requiring enablement * checks too often. * The important fix is that even when it has listeners, after firing PROP_ENABLED * in response to a selection change, it should not actually compute the enablement * status again until someone asks isEnabled(). Otherwise it will perpetually be * firing changes, when in fact no one cares (the action is not even visible). * @see "#13505" */ public void testNoRedundantEnablementChecks() throws Exception { LazyNodeAction a = (LazyNodeAction)SystemAction.get(LazyNodeAction.class); ActionsInfraHid.WaitPCL l = new ActionsInfraHid.WaitPCL(NodeAction.PROP_ENABLED); try { assertEquals(0, a.count); assertFalse(a.listeners); assertFalse(a.isEnabled()); a.addPropertyChangeListener(l); assertTrue(a.listeners); assertFalse(a.isEnabled()); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n1}); assertTrue(l.changed()); l.gotit = 0; assertTrue(a.isEnabled()); // Now make sure calls to isEnabled() do not do anything while the selection has not changed. a.count = 0; assertTrue(a.isEnabled()); assertEquals("Adjacent calls to isEnabled() do not recheck the same node selection", 0, a.count); /* This is pretty irrelevant, it probably never happens anyway: // Make sure equivalent node arrays are not considered significant. ActionsInfraHid.UT.setCurrentNodes(new Node[] {n1}); if (!l.changed()) { Thread.sleep(1000); } l.gotit = 0; assertTrue(a.isEnabled()); assertEquals("Adjacent calls to isEnabled() do not recheck equivalent node selections", 0, a.count); */ // But a real change is significant and enable(Node[]) is checked. ActionsInfraHid.UT.setCurrentNodes(new Node[] {n2}); if (!l.changed()) { Thread.sleep(1000); } l.gotit = 0; assertTrue(a.isEnabled()); assertEquals("A real change to selection calls enable(Node[]) again", 1, a.count); // No checks made just because there was a selection change, but no request. a.count = 0; ActionsInfraHid.UT.setCurrentNodes(new Node[] {n1, n3}); assertTrue(l.changed()); l.gotit = 0; assertEquals("Do not make extra checks until someone asks", 0, a.count); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n2, n3}); assertTrue("Do not keep firing changes when nobody is paying attention", !l.changed()); // After detaching all listeners, selection changes are not tracked more than once. a.removePropertyChangeListener(l); assertFalse(a.listeners); ActionsInfraHid.UT.setCurrentNodes(new Node[] {}); Thread.sleep(1000); assertFalse(a.isEnabled()); a.count = 0; assertFalse(a.isEnabled()); assertEquals("Even with no listeners, adjacent isEnabled()s are clean", 0, a.count); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n3}); Thread.sleep(1000); assertEquals("With no listeners, node selection changes are ignored", 0, a.count); assertTrue(a.isEnabled()); assertEquals("With no listeners, isEnabled() works on demand", 1, a.count); } finally { a.removePropertyChangeListener(l); ActionsInfraHid.UT.setCurrentNodes(new Node[0]); ActionsInfraHid.UT.setCurrentNodes(null); a.count = 0; } } /** Due to lack of a coherent API in NodeAction for telling it that any previous * results with a given node selection are now void, some subclasses such as * CookieAction and Move{Up,Down}Action actually call setEnabled directly, when * some aspect of the selected nodes changes without the selection itself changing. * Make sure that such changes are respected - they had best not call enable() as * typically the subclass itself does the new check anyway - but a subsequent selection * must call enable() again. */ public void testCallSetEnabledDirectly() throws Exception { SimpleNodeAction a1 = (SimpleNodeAction)SystemAction.get(SimpleNodeAction.class); ActionsInfraHid.WaitPCL l = new ActionsInfraHid.WaitPCL(NodeAction.PROP_ENABLED); try { assertFalse(a1.isEnabled()); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n1}); assertTrue(a1.isEnabled()); n1.setDisplayName("foo"); a1.setEnabled(false); assertFalse(a1.isEnabled()); n1.setDisplayName("text"); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n2}); assertTrue(a1.isEnabled()); // Now try it with listeners. a1.addPropertyChangeListener(l); assertTrue(a1.isEnabled()); n2.setDisplayName("foo"); a1.setEnabled(false); assertTrue(l.changed()); l.gotit = 0; assertFalse(a1.isEnabled()); n2.setDisplayName("text"); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n1}); assertTrue(l.changed()); l.gotit = 0; assertTrue(a1.isEnabled()); n1.setDisplayName("foo"); a1.setEnabled(false); assertTrue(l.changed()); l.gotit = 0; assertFalse(a1.isEnabled()); n1.setDisplayName("text"); ActionsInfraHid.UT.setCurrentNodes(new Node[] {n2}); assertTrue(l.changed()); l.gotit = 0; assertTrue(a1.isEnabled()); } finally { a1.removePropertyChangeListener(l); n1.setDisplayName("text"); ActionsInfraHid.UT.setCurrentNodes(new Node[0]); ActionsInfraHid.UT.setCurrentNodes(null); } } // // cloneAction support // public void testNodeActionIsCorrectlyClonned () throws Exception { class MN extends AbstractNode { public MN (String displayName) { super (org.openide.nodes.Children.LEAF); setDisplayName (displayName); } } class Counter implements java.beans.PropertyChangeListener { int cnt; public void propertyChange (java.beans.PropertyChangeEvent ev) { cnt++; } public void assertCnt (String txt, int cnt) { assertEquals (txt, cnt, this.cnt); this.cnt = 0; } } SimpleNodeAction s = (SimpleNodeAction)SimpleNodeAction.get (SimpleNodeAction.class); Counter counter = new Counter (); InstanceContent ic = new InstanceContent (); org.openide.util.lookup.AbstractLookup lookup = new org.openide.util.lookup.AbstractLookup (ic); javax.swing.Action clone = s.createContextAwareInstance(lookup); clone.addPropertyChangeListener(counter); assertTrue ("Not enabled", !clone.isEnabled()); MN mn1 = new MN ("text"); ic.add (mn1); assertTrue ("Enabled", clone.isEnabled ()); counter.assertCnt ("Once change in enabled state", 1); clone.actionPerformed(new java.awt.event.ActionEvent (this, 0, "")); assertEquals ("Has been executed just once: ", 1, SimpleNodeAction.runOn.size ()); Collection c = (Collection)SimpleNodeAction.runOn.iterator ().next (); SimpleNodeAction.runOn.clear(); assertTrue ("Has been executed on mn1", c.contains (mn1)); MN mn2 = new MN ("x"); ic.add (mn2); assertTrue ("Not enabled, because there are two items", !clone.isEnabled ()); counter.assertCnt ("Another change in the state", 1); ic.remove(mn1); assertTrue ("Not enabled, the one item is not named correctly", !clone.isEnabled ()); counter.assertCnt ("No change right now, the action remains disabled", 0); } public static class SimpleNodeAction extends NodeAction { protected boolean enable(Node[] activatedNodes) { boolean r = activatedNodes.length == 1 && activatedNodes[0].getDisplayName().equals("text"); //System.err.println("enable: activatedNodes=" + Arrays.asList(activatedNodes) + " r=" + r); return r; } public static final List runOn = new ArrayList(); // List |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.