openide.util/test/unit/src/org/openide/util/lookup/ProxyLookupTest.java
author Jesse Glick <jglick@netbeans.org>
Fri, 09 Oct 2009 15:11:13 -0400
changeset 833 0e00857c5827
parent 829 3b2ed3e1f01b
permissions -rw-r--r--
Warnings.
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
     5  *
     6  * The contents of this file are subject to the terms of either the GNU
     7  * General Public License Version 2 only ("GPL") or the Common
     8  * Development and Distribution License("CDDL") (collectively, the
     9  * "License"). You may not use this file except in compliance with the
    10  * License. You can obtain a copy of the License at
    11  * http://www.netbeans.org/cddl-gplv2.html
    12  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    13  * specific language governing permissions and limitations under the
    14  * License.  When distributing the software, include this License Header
    15  * Notice in each file and include the License file at
    16  * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    17  * particular file as subject to the "Classpath" exception as provided
    18  * by Sun in the GPL Version 2 section of the License file that
    19  * accompanied this code. If applicable, add the following below the
    20  * License Header, with the fields enclosed by brackets [] replaced by
    21  * your own identifying information:
    22  * "Portions Copyrighted [year] [name of copyright owner]"
    23  *
    24  * Contributor(s):
    25  *
    26  * The Original Software is NetBeans. The Initial Developer of the Original
    27  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
    28  * Microsystems, Inc. All Rights Reserved.
    29  *
    30  * If you wish your version of this file to be governed by only the CDDL
    31  * or only the GPL Version 2, indicate your decision by adding
    32  * "[Contributor] elects to include this software in this distribution
    33  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    34  * single choice of license, a recipient has the option to distribute
    35  * your version of this file under either the CDDL, the GPL Version 2 or
    36  * to extend the choice of license to its licensees as provided above.
    37  * However, if you add GPL Version 2 code and therefore, elected the GPL
    38  * Version 2 license, then the option applies only if the new code is
    39  * made subject to such option by the copyright holder.
    40  */
    41 
    42 package org.openide.util.lookup;
    43 
    44 import java.io.Serializable;
    45 
    46 import java.lang.ref.Reference;
    47 import java.lang.ref.WeakReference;
    48 import java.util.*;
    49 import java.util.concurrent.Executor;
    50 import junit.framework.*;
    51 import org.netbeans.junit.*;
    52 import org.netbeans.modules.openide.util.ActiveQueue;
    53 import org.openide.util.Lookup;
    54 import org.openide.util.Lookup.Result;
    55 import org.openide.util.LookupEvent;
    56 import org.openide.util.LookupListener;
    57 
    58 /** Runs all NbLookupTest tests on ProxyLookup and adds few additional.
    59  */
    60 @SuppressWarnings("unchecked") // XXX ought to be corrected, just a lot of them
    61 public class ProxyLookupTest extends AbstractLookupBaseHid
    62 implements AbstractLookupBaseHid.Impl {
    63     public ProxyLookupTest(java.lang.String testName) {
    64         super(testName, null);
    65     }
    66 
    67     public static Test suite() {
    68         return new NbTestSuite (ProxyLookupTest.class);
    69 //        return new ProxyLookupTest("testDuplicatedLookupArrayIndexWithSetLookupAsInIssue123679");
    70     }
    71     
    72     /** Creates an lookup for given lookup. This class just returns 
    73      * the object passed in, but subclasses can be different.
    74      * @param lookup in lookup
    75      * @return a lookup to use
    76      */
    77     public Lookup createLookup (Lookup lookup) {
    78         return new ProxyLookup (new Lookup[] { lookup });
    79     }
    80     
    81     public Lookup createInstancesLookup (InstanceContent ic) {
    82         return new AbstractLookup (ic);
    83     }
    84     
    85 
    86     public void clearCaches () {
    87     }    
    88     
    89     
    90     /** Check whether setLookups method does not fire when there is no
    91      * change in the lookups.
    92      */
    93     public void testProxyListener () {
    94         ProxyLookup lookup = new ProxyLookup (new Lookup[0]);
    95 
    96         final Lookup.Template<Object> template = new Lookup.Template<Object>(Object.class);
    97         final Object[] IGNORE = {
    98             ProxyLookup.ImmutableInternalData.EMPTY,
    99             ProxyLookup.ImmutableInternalData.EMPTY_ARR,
   100             ActiveQueue.queue(),
   101             Collections.emptyMap(),
   102             Collections.emptyList(),
   103             Collections.emptySet()
   104         };
   105         
   106         assertSize("Pretty small", Collections.singleton(lookup), 16, IGNORE);
   107         
   108         Lookup.Result<Object> res = lookup.lookup (template);
   109 
   110         assertSize("Bigger", Collections.singleton(lookup), 216, IGNORE);
   111         
   112         LL ll = new LL ();
   113         res.addLookupListener (ll);
   114         Collection allRes = res.allInstances ();
   115         
   116         lookup.setLookups (new Lookup[0]);
   117         
   118         if (ll.getCount () != 0) {
   119            fail ("Calling setLookups (emptyarray) fired a change");
   120         }
   121         
   122         InstanceContent t = new InstanceContent();
   123         Lookup del = new AbstractLookup (t);
   124         t.add("Ahoj");
   125         lookup.setLookups (new Lookup[] { del });
   126         
   127         if (ll.getCount () != 1) {
   128             fail ("Changing lookups did not generate an event");
   129         }
   130         
   131         lookup.setLookups (new Lookup[] { del });
   132         
   133         if (ll.getCount () != 0) {
   134            fail ("Calling setLookups (thesamearray) fired a change");
   135         }
   136     }
   137     
   138     public void testNoListenersProxyListener () {
   139         ProxyLookup lookup = new ProxyLookup (new Lookup[0]);
   140         class E implements Executor {
   141             Runnable r;
   142             public void execute(Runnable command) {
   143                 assertNull("NO previous", r);
   144                 r = command;
   145             }
   146             public void perform() {
   147                 assertNotNull("We shall have a runnable", r);
   148                 r.run();
   149                 r = null;
   150             }
   151         }
   152         E executor = new E();
   153                 
   154 
   155         final Lookup.Template<Object> template = new Lookup.Template<Object>(Object.class);
   156         final Object[] IGNORE = {
   157             ProxyLookup.ImmutableInternalData.EMPTY,
   158             ProxyLookup.ImmutableInternalData.EMPTY_ARR,
   159             ActiveQueue.queue(),
   160             Collections.emptyMap(),
   161             Collections.emptyList(),
   162             Collections.emptySet()
   163         };
   164         
   165         assertSize("Pretty small", Collections.singleton(lookup), 16, IGNORE);
   166         
   167         Lookup.Result<Object> res = lookup.lookup (template);
   168 
   169         assertSize("Bigger", Collections.singleton(lookup), 216, IGNORE);
   170         
   171         LL ll = new LL ();
   172         res.addLookupListener (ll);
   173         Collection allRes = res.allInstances ();
   174         
   175         lookup.setLookups (executor, new Lookup[0]);
   176         if (ll.getCount () != 0) {
   177            fail ("Calling setLookups (emptyarray) fired a change");
   178         }
   179         
   180         InstanceContent t = new InstanceContent();
   181         Lookup del = new AbstractLookup (t);
   182         t.add("Ahoj");
   183         lookup.setLookups (executor, new Lookup[] { del });
   184         assertEquals("No change yet", 0, ll.getCount());
   185         executor.perform();
   186         if (ll.getCount () != 1) {
   187             fail ("Changing lookups did not generate an event");
   188         }
   189         
   190         lookup.setLookups (executor, new Lookup[] { del });
   191         if (ll.getCount () != 0) {
   192            fail ("Calling setLookups (thesamearray) fired a change");
   193         }
   194     }
   195 
   196     public void testSetLookups () throws Exception {
   197         AbstractLookup a1 = new AbstractLookup (new InstanceContent ());
   198         AbstractLookup a2 = new AbstractLookup (new InstanceContent ());
   199         
   200         InstanceContent i3 = new InstanceContent ();
   201         i3.add (i3);
   202         AbstractLookup a3 = new AbstractLookup (i3);
   203 
   204         final ProxyLookup p = new ProxyLookup (new Lookup[] { a1, a2 });
   205         final Lookup.Result res1 = p.lookup (new Lookup.Template (Object.class));
   206         Collection c1 = res1.allInstances();
   207         
   208         Lookup.Result res2 = p.lookup (new Lookup.Template (String.class));
   209         Collection c2 = res2.allInstances ();
   210         
   211         
   212         assertTrue ("We need two results", res1 != res2);
   213 
   214         final Object blocked = new Object ();
   215 
   216         class L extends Object implements LookupListener {
   217             public void resultChanged (LookupEvent ev) {
   218                 try {
   219                     res1.removeLookupListener(this);
   220                     
   221                     // waiting for second thread to start #111#
   222                     blocked.wait ();
   223 
   224                 } catch (Exception ex) {
   225                     ex.printStackTrace();
   226                     fail ("An exception occured ");
   227                 }
   228             }
   229         }
   230         
   231         final L listener1 = new L ();
   232         res1.addLookupListener (listener1);
   233         
   234 
   235         Runnable newLookupSetter = new Runnable() {
   236             public void run () {
   237                 synchronized (blocked) {
   238                     try {
   239                         p.setLookups (new Lookup[0]);
   240                     } catch (Exception ex) {
   241                         ex.printStackTrace();
   242                         fail ("setLookups failed.");
   243                     } finally {
   244                         // starts the main thread #111#
   245                         blocked.notify ();
   246                     }
   247                 }
   248             }
   249         };
   250         
   251         synchronized (blocked) {
   252             new Thread (newLookupSetter).start ();
   253             
   254             p.setLookups (new Lookup[] { a1, a2, a3 });
   255         }
   256     }
   257     
   258     public void testProxyLookupTemplateCaching(){
   259         Lookup lookups[] = new Lookup[1];
   260         doProxyLookupTemplateCaching(lookups, false);
   261     }
   262     
   263     public void testProxyLookupTemplateCachingOnSizeTwoArray() {
   264         Lookup lookups[] = new Lookup[2];
   265         lookups[1] = Lookup.EMPTY;
   266         doProxyLookupTemplateCaching(lookups, false);
   267     }
   268     public void testProxyLookupShallNotAllowModificationOfGetLookups(){
   269         Lookup lookups[] = new Lookup[1];
   270         doProxyLookupTemplateCaching(lookups, true);
   271     }
   272     
   273     public void testProxyLookupShallNotAllowModificationOfGetLookupsOnSizeTwoArray() {
   274         Lookup lookups[] = new Lookup[2];
   275         lookups[1] = Lookup.EMPTY;
   276         doProxyLookupTemplateCaching(lookups, true);
   277     }
   278     
   279     /** Index 0 of lookups will be modified, the rest is up to the 
   280      * setup code.
   281      */
   282     private void doProxyLookupTemplateCaching(Lookup[] lookups, boolean reget) {
   283         // Create MyProxyLookup with one lookup containing the String object
   284         InstanceContent inst = new InstanceContent();
   285         inst.add(new String("Hello World")); //NOI18N
   286         lookups[0] = new AbstractLookup(inst);
   287         ProxyLookup proxy = new ProxyLookup(lookups);
   288         if (reget) {
   289             lookups = proxy.getLookups();
   290         }
   291         
   292         // Performing template lookup for String object
   293         Lookup.Result result = proxy.lookup(new Lookup.Template(String.class, null, null));
   294         int stringTemplateResultSize = result.allInstances().size();
   295         assertEquals ("Ensure, there is only one instance of String.class in proxyLookup:", //NOI18N
   296                 1, stringTemplateResultSize);
   297         
   298         // Changing lookup in proxy lookup, now it will contain 
   299         // StringBuffer Object instead of String
   300         InstanceContent ic2 = new InstanceContent();
   301         ic2.add(new Integer(1234567890));
   302         lookups[0] = new AbstractLookup(ic2);
   303         proxy.setLookups(lookups);
   304         
   305         assertEquals ("the old result is updated", 0, result.allInstances().size());
   306 
   307         // Instance of String.class should not appear in proxyLookup
   308         Lookup.Result r2 = proxy.lookup(new Lookup.Template(String.class, null, null));
   309         assertEquals ("Instance of String.class should not appear in proxyLookup:", //NOI18N
   310                 0, r2.allInstances().size());
   311 
   312         Lookup.Result r3 = proxy.lookup(new Lookup.Template(Integer.class, null, null));
   313         assertEquals ("There is only one instance of Integer.class in proxyLookup:", //NOI18N
   314                 1, r3.allInstances().size());
   315     }
   316     
   317     public void testListeningAndQueryingByTwoListenersInstancesSetLookups() {
   318         doListeningAndQueryingByTwoListenersSetLookups(0, 1);
   319     }
   320     public void testListeningAndQueryingByTwoListenersClassesSetLookups() {
   321         doListeningAndQueryingByTwoListenersSetLookups(1, 1);        
   322     }
   323     public void testListeningAndQueryingByTwoListenersItemsSetLookups() {
   324         doListeningAndQueryingByTwoListenersSetLookups(2, 1);
   325     }
   326     
   327     public void testListeningAndQueryingByTwoListenersInstancesSetLookups2() {
   328         doListeningAndQueryingByTwoListenersSetLookups(0, 2);
   329     }
   330     public void testListeningAndQueryingByTwoListenersClassesSetLookups2() {
   331         doListeningAndQueryingByTwoListenersSetLookups(1, 2);        
   332     }
   333     public void testListeningAndQueryingByTwoListenersItemsSetLookups2() {
   334         doListeningAndQueryingByTwoListenersSetLookups(2, 2);
   335     }
   336     public void testListeningAndQueryingByTwoListenersInstancesSetLookups22() {
   337         doListeningAndQueryingByTwoListenersSetLookups(0, 22);
   338     }
   339     public void testListeningAndQueryingByTwoListenersClassesSetLookups22() {
   340         doListeningAndQueryingByTwoListenersSetLookups(1, 22);        
   341     }
   342     public void testListeningAndQueryingByTwoListenersItemsSetLookups22() {
   343         doListeningAndQueryingByTwoListenersSetLookups(2, 22);
   344     }
   345     
   346     private void doListeningAndQueryingByTwoListenersSetLookups(final int type, int depth) {
   347         ProxyLookup orig = new ProxyLookup();
   348         ProxyLookup on = orig;
   349         
   350         while (--depth > 0) {
   351             on = new ProxyLookup(new Lookup[] { on });
   352         }
   353         
   354         
   355         final ProxyLookup lookup = on;
   356         
   357         class L implements LookupListener {
   358             Lookup.Result integer = lookup.lookup(new Lookup.Template(Integer.class));
   359             Lookup.Result number = lookup.lookup(new Lookup.Template(Number.class));
   360             Lookup.Result serial = lookup.lookup(new Lookup.Template(Serializable.class));
   361             
   362             {
   363                 integer.addLookupListener(this);
   364                 number.addLookupListener(this);
   365                 serial.addLookupListener(this);
   366             }
   367             
   368             int round;
   369             
   370             public void resultChanged(LookupEvent ev) {
   371                 Collection c1 = get(type, integer);
   372                 Collection c2 = get(type, number);
   373                 Collection c3 = get(type, serial);
   374                 
   375                 assertEquals("round " + round + " c1 vs. c2", c1, c2);
   376                 assertEquals("round " + round + " c1 vs. c3", c1, c3);
   377                 assertEquals("round " + round + " c2 vs. c3", c2, c3);
   378                 
   379                 round++;
   380             }            
   381 
   382             private Collection get(int type, Lookup.Result res) {
   383                 Collection c;
   384                 switch(type) {
   385                     case 0: c = res.allInstances(); break;
   386                     case 1: c = res.allClasses(); break;
   387                     case 2: c = res.allItems(); break;
   388                     default: c = null; fail("Type: " + type); break;
   389                 }
   390                 
   391                 assertNotNull(c);
   392                 return new ArrayList(c);
   393             }
   394         }
   395         
   396         L listener = new L();
   397         listener.resultChanged(null);
   398         ArrayList arr = new ArrayList();
   399         for(int i = 0; i < 100; i++) {
   400             arr.add(new Integer(i));
   401             
   402             orig.setLookups(new Lookup[] { Lookups.fixed(arr.toArray()) });
   403         }
   404         
   405         assertEquals("3x100+1 checks", 301, listener.round);
   406     }
   407 
   408     static Object holder;
   409     
   410     public void testProxyWithLiveResultCanBeCollected() {
   411         Lookup layer0 = Lookups.singleton("Hello");
   412         Lookup layer1 = new ProxyLookup(new Lookup[] { layer0 });
   413         Lookup layer2 = new ProxyLookup(new Lookup[] { layer1 });
   414         Lookup.Result result1 = layer1.lookup(new Lookup.Template(String.class));
   415 
   416         assertEquals("One instance", 1, result1.allInstances().size());
   417 
   418         // this will create ProxyLookup$R which listens on origResult
   419         Lookup.Result result2 = layer2.lookup(new Lookup.Template(String.class));
   420         
   421         // this line is necessary. W/o actually querying the result,
   422         // it will nether compute it nor attach the listener.
   423         assertEquals("One instance", 1, result2.allInstances().size());
   424         
   425         result2.addLookupListener(new LookupListener() {
   426             public void resultChanged(LookupEvent ev) {}
   427         });
   428         
   429         Reference ref = new WeakReference(layer2);
   430         layer2 = null;
   431         result2 = null;
   432         try {
   433             holder = result1;
   434             assertGC ("The proxy lookup not been garbage collected!", ref);
   435         } finally {
   436             holder = null;
   437         }
   438     }
   439     
   440     public void testArrayIndexAsInIssue119292() throws Exception {
   441         final ProxyLookup pl = new ProxyLookup();
   442         final int[] cnt = { 0 };
   443         
   444         class L extends Lookup {
   445             L[] set;
   446             Lookup l;
   447             
   448             public L(String s) {
   449                 l = Lookups.singleton(s);
   450             }
   451             
   452             @Override
   453             public <T> T lookup(Class<T> clazz) {
   454                 return l.lookup(clazz);
   455             }
   456 
   457             @Override
   458             public <T> Result<T> lookup(Template<T> template) {
   459                 return l.lookup(template);
   460             }
   461 
   462             @Override
   463             @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
   464             public boolean equals(Object obj) {
   465                 if (set != null) {
   466                     cnt[0]++;
   467                     pl.setLookups(set);
   468                 }
   469                 return super.equals(obj);
   470             }
   471 
   472             @Override
   473             public int hashCode() {
   474                 int hash = 3;
   475                 return hash;
   476             }
   477         }
   478 
   479         Result<String> res = pl.lookupResult(String.class);
   480         assertEquals(Collections.EMPTY_LIST, res.allItems());
   481         
   482         L[] old = { new L("A"), new L("B") };
   483         L[] now = { new L("C") };
   484         
   485         pl.setLookups(old);
   486         cnt[0] = 0;
   487         
   488         old[0].set = new L[0];
   489         pl.setLookups(now);
   490 
   491         assertEquals("No call to equals", 0, cnt[0]);
   492         
   493         assertEquals("Still assigned to C", Collections.singletonList("C"), res.allInstances());
   494     }
   495     
   496     public void testArrayIndexWithAddRemoveListenerAsInIssue119292() throws Exception {
   497         final ProxyLookup pl = new ProxyLookup();
   498         final int[] cnt = { 0 };
   499         
   500         class L extends Lookup {
   501             L[] set;
   502             Lookup l;
   503             
   504             public L(String s) {
   505                 l = Lookups.singleton(s);
   506             }
   507             
   508             @Override
   509             public <T> T lookup(Class<T> clazz) {
   510                 return l.lookup(clazz);
   511             }
   512 
   513             @Override
   514             public <T> Result<T> lookup(Template<T> template) {
   515                 Result<T> r = l.lookup(template);
   516                 return new R<T>(r);
   517             }
   518 
   519             final class R<T> extends Result<T> {
   520                 private Result<T> delegate;
   521 
   522                 public R(Result<T> delegate) {
   523                     this.delegate = delegate;
   524                 }
   525                 
   526                 @Override
   527                 public void addLookupListener(LookupListener l) {
   528                     cnt[0]++;
   529                     if (set != null) {
   530                         pl.setLookups(set);
   531                     }
   532                     delegate.addLookupListener(l);
   533                 }
   534 
   535                 @Override
   536                 public void removeLookupListener(LookupListener l) {
   537                     cnt[0]++;
   538                     if (set != null) {
   539                         pl.setLookups(set);
   540                     }
   541                     delegate.removeLookupListener(l);
   542                 }
   543 
   544                 @Override
   545                 public Collection<? extends T> allInstances() {
   546                     return delegate.allInstances();
   547                 }
   548             }
   549         }
   550 
   551         Result<String> res = pl.lookupResult(String.class);
   552         assertEquals(Collections.EMPTY_LIST, res.allItems());
   553         
   554         L[] old = { new L("A"), new L("B") };
   555         L[] now = { new L("C") };
   556         
   557         pl.setLookups(old);
   558         cnt[0] = 0;
   559         
   560         old[0].set = new L[0];
   561         pl.setLookups(now);
   562 
   563         if (cnt[0] == 0) {
   564             fail("There should be calls to listeners");
   565         }
   566         
   567         assertEquals("C is overriden from removeLookupListener", Collections.emptyList(), res.allInstances());
   568     }
   569     
   570     
   571     public void testArrayIndexWithSetLookupAsInIssue123679() throws Exception {
   572         final ProxyLookup pl = new ProxyLookup();
   573         final int[] cnt = { 0 };
   574         
   575         class L extends Lookup {
   576             L[] set;
   577             Lookup l;
   578             Collection<? extends Serializable> res;
   579             
   580             public L(String s) {
   581                 l = Lookups.singleton(s);
   582             }
   583             
   584             @Override
   585             public <T> T lookup(Class<T> clazz) {
   586                 return l.lookup(clazz);
   587             }
   588 
   589             @Override
   590             public <T> Result<T> lookup(Template<T> template) {
   591                 cnt[0]++;
   592                 if (set != null) {
   593                     pl.setLookups(set);
   594                     res = pl.lookupAll(Serializable.class);
   595                 }
   596                 Result<T> r = l.lookup(template);
   597                 return r;
   598             }
   599         }
   600 
   601         L[] now = { new L("A"), new L("B") };
   602         L[] old = { new L("C") };
   603         pl.setLookups(old);
   604         old[0].set = now;
   605         
   606         Result<String> res = pl.lookupResult(String.class);
   607         assertEquals("New items visible", 2, res.allItems().size());
   608         
   609         
   610         pl.setLookups(new L("X"), new L("Y"), new L("Z"));
   611     }
   612     
   613     public void testDuplicatedLookupArrayIndexWithSetLookupAsInIssue123679() throws Exception {
   614         final ProxyLookup pl = new ProxyLookup();
   615         final int[] cnt = { 0 };
   616         
   617         class L extends Lookup {
   618             L[] set;
   619             Lookup l;
   620             Collection<? extends Serializable> res;
   621             
   622             public L(String s) {
   623                 l = Lookups.singleton(s);
   624             }
   625             
   626             @Override
   627             public <T> T lookup(Class<T> clazz) {
   628                 return l.lookup(clazz);
   629             }
   630 
   631             @Override
   632             public <T> Result<T> lookup(Template<T> template) {
   633                 cnt[0]++;
   634                 if (set != null) {
   635                     pl.setLookups(set);
   636                     res = pl.lookupAll(Serializable.class);
   637                 }
   638                 Result<T> r = l.lookup(template);
   639                 return r;
   640             }
   641         }
   642 
   643         L dupl = new L("A");
   644         L[] now = { dupl };
   645         L[] old = { new L("C") };
   646         pl.setLookups(old);
   647         old[0].set = now;
   648         
   649         Result<String> res = pl.lookupResult(String.class);
   650         assertEquals("New items visible", 1, res.allItems().size());
   651         
   652         
   653         pl.setLookups(old);
   654     }
   655 }