001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.junit;
043:
044: import java.awt.EventQueue;
045: import java.lang.reflect.Method;
046: import java.util.Iterator;
047: import java.util.concurrent.ExecutorService;
048: import java.util.concurrent.Executors;
049: import junit.framework.Test;
050: import junit.framework.TestCase;
051: import org.openide.util.Lookup;
052: import org.openide.util.LookupEvent;
053: import org.openide.util.LookupListener;
054:
055: public abstract class MockServicesTest extends TestCase {
056:
057: protected MockServicesTest(String name) {
058: super (name);
059: }
060:
061: public static Test suite() {
062: NbTestSuite s = new NbTestSuite();
063: s.addTestSuite(JreTest.class);
064: s.addTestSuite(LookupTest.class);
065: return s;
066: }
067:
068: public interface Choice {
069: String value();
070: }
071:
072: protected abstract <T> Iterator<? extends T> lookup(Class<T> clazz);
073:
074: protected abstract void assertChangesFired(int countOfChanges);
075:
076: private String getChoice() {
077: Iterator<? extends Choice> it = lookup(Choice.class);
078: if (it.hasNext()) {
079: Choice c = it.next();
080: if (it.hasNext()) {
081: throw new IllegalStateException(
082: "have >1 instance available: " + c + " vs. "
083: + it.next());
084: }
085: return c.value();
086: } else {
087: return "default";
088: }
089: }
090:
091: /**
092: * Basic test that services are set.
093: */
094: public void testGetChoice() {
095: MockServices.setServices();
096: assertChangesFired(1);
097: assertEquals("initial value", "default", getChoice());
098: MockServices.setServices(MockChoice1.class);
099: assertEquals("registered value", "mock1", getChoice());
100: assertChangesFired(1);
101: MockServices.setServices(MockChoice2.class);
102: assertEquals("registered value", "mock2", getChoice());
103: assertChangesFired(1);
104: MockServices.setServices(MockChoice1.class, MockChoice2.class);
105: assertChangesFired(1);
106: try {
107: getChoice();
108: fail("Should not work on >1 choice");
109: } catch (IllegalStateException x) {
110: }
111: }
112:
113: public static final class MockChoice1 implements Choice {
114: public MockChoice1() {
115: }
116:
117: public String value() {
118: return "mock1";
119: }
120: }
121:
122: public static final class MockChoice2 implements Choice {
123: public MockChoice2() {
124: }
125:
126: public String value() {
127: return "mock2";
128: }
129: }
130:
131: /**
132: * Check that static registrations in META-INF/services/* continue to be
133: * available as services - but with lower priority than the explicitly
134: * registered ones.
135: */
136: public void testBackgroundServicesStillAvailable() {
137: MockServices.setServices();
138: Iterator<? extends DummyService> i = lookup(DummyService.class);
139: assertTrue("statically registered service available", i
140: .hasNext());
141: assertEquals("of correct type", DummyServiceImpl.class, i
142: .next().getClass());
143: assertFalse("but no more", i.hasNext());
144: MockServices.setServices(DummyServiceImpl2.class);
145: i = lookup(DummyService.class);
146: assertTrue("custom service registered", i.hasNext());
147: assertEquals("before static service", DummyServiceImpl2.class,
148: i.next().getClass());
149: assertTrue("then static service", i.hasNext());
150: assertEquals("of static type", DummyServiceImpl.class, i.next()
151: .getClass());
152: assertFalse("and that is all", i.hasNext());
153: }
154:
155: public static final class DummyServiceImpl2 implements DummyService {
156: }
157:
158: /**
159: * Ensure that attempts to register classes which are not publicly
160: * instantiable fail immediately.
161: */
162: public void testModifierRestrictions() {
163: try {
164: MockServices.setServices(MockChoice3.class);
165: fail("Should not permit nonpublic class to be registered");
166: } catch (IllegalArgumentException x) {/* right */
167: }
168: try {
169: MockServices.setServices(MockChoice4.class);
170: fail("Should not permit class w/o public constructor to be registered");
171: } catch (IllegalArgumentException x) {/* right */
172: }
173: try {
174: MockServices.setServices(MockChoice5.class);
175: fail("Should not permit class w/o no-arg constructor to be registered");
176: } catch (IllegalArgumentException x) {/* right */
177: }
178: try {
179: MockServices.setServices(MockChoice6.class);
180: fail("Should not permit abstract class to be registered");
181: } catch (IllegalArgumentException x) {/* right */
182: }
183: try {
184: MockServices.setServices(Choice.class);
185: fail("Should not permit interface to be registered");
186: } catch (IllegalArgumentException x) {/* right */
187: }
188: }
189:
190: private static final class MockChoice3 implements Choice {
191: public MockChoice3() {
192: }
193:
194: public String value() {
195: return "mock3";
196: }
197: }
198:
199: public static final class MockChoice4 implements Choice {
200: MockChoice4() {
201: }
202:
203: public String value() {
204: return "mock4";
205: }
206: }
207:
208: public static final class MockChoice5 implements Choice {
209: public MockChoice5(String v) {
210: }
211:
212: public String value() {
213: return "mock5";
214: }
215: }
216:
217: public static abstract class MockChoice6 implements Choice {
218: }
219:
220: /**
221: * Check that service registrations are available from all threads,
222: * not just the thread calling setServices.
223: */
224: public void testOtherThreads() throws Exception {
225: // Ensure EQ thread exists. This will not be a child of current thread group.
226: EventQueue.invokeAndWait(new Runnable() {
227: public void run() {
228: }
229: });
230: MockServices.setServices(MockChoice1.class);
231: EventQueue.invokeAndWait(new Runnable() {
232: public void run() {
233: assertEquals("registered value in EQ", "mock1",
234: getChoice());
235: }
236: });
237: // This will be a child of current thread group.
238: ExecutorService svc = Executors.newSingleThreadExecutor();
239: svc.submit(new Runnable() {
240: public void run() {
241: assertEquals("registered value in thread pool",
242: "mock1", getChoice());
243: }
244: }).get();
245: MockServices.setServices(MockChoice2.class);
246: EventQueue.invokeAndWait(new Runnable() {
247: public void run() {
248: assertEquals("new registered value in EQ", "mock2",
249: getChoice());
250: }
251: });
252: svc.submit(new Runnable() {
253: public void run() {
254: assertEquals("new registered value in thread pool",
255: "mock2", getChoice());
256: }
257: }).get();
258: }
259:
260: /**
261: * Check that services classes can be registered even if they are not
262: * loadable by the class loader which loaded MockServices.class.
263: * /
264: public void testInstancesFromDerivativeClassLoaders() {
265: // XXX currently will throw assertion errors
266: }
267: */
268:
269: public static class JreTest extends MockServicesTest {
270:
271: public JreTest(String s) {
272: super (s);
273: }
274:
275: @SuppressWarnings("unchecked")
276: // using reflection
277: protected <T> Iterator<? extends T> lookup(Class<T> clazz) {
278: try {
279: Class serviceLoader = Class
280: .forName("java.util.ServiceLoader");
281: Method load = serviceLoader.getMethod("load",
282: Class.class);
283: Object loader = load.invoke(null, clazz);
284: return ((Iterable) loader).iterator();
285: } catch (Exception x1) {
286: try {
287: Class service = Class.forName("sun.misc.Service");
288: Method providers = service.getMethod("providers",
289: Class.class);
290: return (Iterator) providers.invoke(null, clazz);
291: } catch (Exception x2) {
292: throw (AssertionError) new AssertionError(
293: "Neither java.util.ServiceLoader nor sun.misc.Service available")
294: .initCause(x1.initCause(x2));
295: }
296: }
297: }
298:
299: protected void assertChangesFired(int countOfChanges) {
300: // no changes listening supported
301: }
302:
303: }
304:
305: public static class LookupTest extends MockServicesTest implements
306: LookupListener {
307: private Lookup.Result<?> res;
308: private int cnt;
309:
310: public LookupTest(String s) {
311: super (s);
312: res = Lookup.getDefault().lookupResult(Object.class);
313: res.addLookupListener(this );
314: res.allInstances();
315: }
316:
317: protected <T> Iterator<? extends T> lookup(Class<T> clazz) {
318: return Lookup.getDefault().lookupAll(clazz).iterator();
319: }
320:
321: protected void assertChangesFired(int countOfChanges) {
322: if (countOfChanges <= cnt) {
323: cnt = 0;
324: } else {
325: fail("Not enough changes fired: " + cnt + " expected: "
326: + countOfChanges);
327: }
328: }
329:
330: public void resultChanged(LookupEvent ev) {
331: cnt++;
332: }
333:
334: }
335:
336: }
|