/* * Copyright 2008 the original author or authors. * * 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.rioproject.impl.watch; import org.junit.Assert; import net.jini.config.Configuration; import net.jini.config.EmptyConfiguration; import org.junit.Test; import org.junit.runner.JUnitCore; import org.rioproject.watch.*; import java.util.*; /** * The class tests the <code>WatchDataSourceRegistry</code> class against * its javadoc specification. The class tests the public constructors and * methods declared in <code>WatchDataSourceRegistry</code>. * <p> * This test does not test: * <ul> * <li>Duplicated watch IDs * <li>Duplicated watches and listeners, that is, registering (or * deregistering) same watches and listeners more than once * </ul> * These conditions are considered abnormal and related * <code>WatchDataSourceRegistry</code> behavior is considered undefined. */ public class WatchDataSourceRegistryTest { /** * Tests the <code>WatchDataSourceRegistry()</code> constructor. */ @Test public void testConstructor() { new WatchDataSourceRegistry(); } public static void main(String... args) { JUnitCore.main(WatchDataSourceRegistryTest.class.getName()); } /** * Tests the <code>closeAll()</code> method. */ @Test public void testCloseAll() { WatchDataSourceRegistry registry = new WatchDataSourceRegistry(); int[] counts = new int[] {0, 1, 10, 100}; for (int count : counts) { // Setup watches Collection<Watch> watches = new ArrayList<Watch>(); for (int j = 0; j < count; j++) { String id = "watch" + j; LoggingWatchDataSource ds = new LoggingWatchDataSource( id, EmptyConfiguration.INSTANCE); Watch watch = new GaugeWatch(ds, id); watches.add(watch); } // Populate registry for (Watch watch : watches) { registry.register(watch); } // Test closeAll() registry.closeAll(); for (Watch watch : watches) { LoggingWatchDataSource ds = (LoggingWatchDataSource) watch.getWatchDataSource(); checkLog(ds.log(), "close()"); } // Clear registry for (Watch watch : watches) { registry.deregister(watch); LoggingWatchDataSource ds = (LoggingWatchDataSource) watch.getWatchDataSource(); checkLog(ds.log(), "close()"); // deregister calls close } // Test closeAll() registry.closeAll(); for (Watch watch : watches) { LoggingWatchDataSource ds = (LoggingWatchDataSource) watch.getWatchDataSource(); checkLog(ds.log(), ""); } } } /** * Tests the <code>register(Watch)</code> and * <code>deregister(Watch)</code> methods. */ @Test public void testRegisterDeregister() { WatchDataSourceRegistry registry = new WatchDataSourceRegistry(); int[] counts = new int[] {0, 1, 10, 100}; for (int count : counts) { Collection<Watch> watches = new ArrayList<Watch>(); for (int k = 0; k < count; k++) { String id = "watch" + k; // Try empty name if (k == 1) { id = ""; } // We are specifying the data source explicitly because // otherwise every watch would use an exported proxy wich // gets unexported when the watch is deregistered, // therefore emitting annoying exceptions and slowing // the test badly WatchDataSource ds = new WatchDataSourceImpl(id, EmptyConfiguration.INSTANCE); watches.add(new GaugeWatch(ds, id)); } // Try registering/deregistering several times for (int j = 0; j < 3; j++) { Assert.assertEquals(0, registry.fetch().length); for (Watch watch : watches) { String id = watch.getId(); Assert.assertNull(registry.findWatch(id)); } for (Watch watch : watches) { registry.register(watch); } Assert.assertEquals(watches.size(), registry.fetch().length); for (Watch watch : watches) { String id = watch.getId(); Watch actual = registry.findWatch(id); Assert.assertNotNull(actual); Assert.assertSame(watch, actual); } for (Watch watch : watches) { registry.deregister(watch); } Assert.assertEquals(0, registry.fetch().length); for (Watch watch : watches) { String id = watch.getId(); Assert.assertNull(registry.findWatch(id)); } } } try { registry.register((Watch[])null); Assert.fail("IllegalArgumentException expected but not thrown"); } catch (IllegalArgumentException e) { } try { registry.deregister((Watch[])null); Assert.fail("IllegalArgumentException expected but not thrown"); } catch (IllegalArgumentException e) { } } /** * Tests the <code>fetch()</code> method. */ @Test public void testFetch1() { WatchDataSourceRegistry registry = new WatchDataSourceRegistry(); int[] counts = new int[] {0, 1, 10, 100}; for (int count : counts) { Set<Watch> watches = new HashSet<Watch>(); Set<WatchDataSource> sources = new HashSet<WatchDataSource>(); for (int k = 0; k < count; k++) { String id = "watch" + k; // Try empty name if (k == 1) { id = ""; } WatchDataSource ds = new WatchDataSourceImpl( id, EmptyConfiguration.INSTANCE); Watch watch = new GaugeWatch(ds, id); watches.add(watch); sources.add(ds); } // Try registering/deregistering several times for (int j = 0; j < 5; j++) { Assert.assertEquals(0, registry.fetch().length); for (Watch watch : watches) { registry.register(watch); } Set<WatchDataSource> res = new HashSet<WatchDataSource>(Arrays.asList(registry.fetch())); Assert.assertEquals(sources, res); for (Watch watch : watches) { registry.deregister(watch); } Assert.assertEquals(0, registry.fetch().length); } } } /** * Tests the <code>fetch(String)</code> method. */ @Test public void testFetch2() { WatchDataSourceRegistry registry = new WatchDataSourceRegistry(); int[] counts = new int[] {0, 1, 10, 100}; for (int count : counts) { Collection<Watch> watches = new ArrayList<Watch>(); for (int k = 0; k < count; k++) { String id = "watch" + k; // Try empty name if (k == 1) { id = ""; } // We are specifying the data source explicitly because // otherwise every watch would use an exported proxy wich // gets unexported when the watch is deregistered, // therefore emitting annoying exceptions and slowing // the test badly WatchDataSource ds = new WatchDataSourceImpl( id, EmptyConfiguration.INSTANCE); watches.add(new GaugeWatch(ds, id)); } // Try registering/deregistering several times for (int j = 0; j < 5; j++) { Assert.assertEquals(0, registry.fetch().length); for (Watch watch : watches) { String id = watch.getId(); WatchDataSource res = registry.fetch(id); Assert.assertNull(res); } for (Watch watch : watches) { registry.register(watch); } Assert.assertEquals(watches.size(), registry.fetch().length); for (Watch watch : watches) { String id = watch.getId(); WatchDataSource res = registry.fetch(id); Assert.assertNotNull(res); Assert.assertSame(watch.getWatchDataSource(), res); } Assert.assertNull(registry.fetch("aaa")); for (Watch watch : watches) { registry.deregister(watch); } Assert.assertEquals(0, registry.fetch().length); for (Watch watch : watches) { String id = watch.getId(); WatchDataSource res = registry.fetch(id); Assert.assertNull(res); } } } try { registry.fetch(null); Assert.fail("IllegalArgumentException expected but not thrown"); } catch (IllegalArgumentException e) { } } /** * Tests the <code>findWatch(String)</code> method. */ @Test public void testFindWatch() { WatchDataSourceRegistry registry = new WatchDataSourceRegistry(); int[] counts = new int[] {0, 1, 10, 100}; for (int count : counts) { Collection<Watch> watches = new ArrayList<Watch>(); for (int k = 0; k < count; k++) { String id = "watch" + k; // Try empty name if (k == 1) { id = ""; } // We are specifying the data source explicitly because // otherwise every watch would use an exported proxy wich // gets unexported when the watch is deregistered, // therefore emitting annoying exceptions and slowing // the test badly WatchDataSource ds = new WatchDataSourceImpl( id, EmptyConfiguration.INSTANCE); watches.add(new GaugeWatch(ds, id)); } // Try registering/deregistering several times for (int j = 0; j < 5; j++) { for (Watch watch : watches) { String id = watch.getId(); Watch res = registry.findWatch(id); Assert.assertNull(res); } for (Watch watch : watches) { registry.register(watch); } for (Watch watch : watches) { String id = watch.getId(); Watch res = registry.findWatch(id); Assert.assertNotNull(res); Assert.assertSame(watch, res); } Assert.assertNull(registry.findWatch("aaa")); for (Watch watch : watches) { registry.deregister(watch); } for (Watch watch : watches) { String id = watch.getId(); Watch res = registry.findWatch(id); Assert.assertNull(res); } } } try { registry.findWatch(null); Assert.fail("IllegalArgumentException expected but not thrown"); } catch (IllegalArgumentException e) { } } /** * Tests the <code>addThresholdListener(String,ThresholdListener)</code> * method. * <p> * This test does not test the non-documented dependency on the * listener type (<code>SLAPolicyHandler</code>-specific behavior). */ @Test public void testAddThresholdListener() { WatchDataSourceRegistry registry = new WatchDataSourceRegistry(); // Test different ordering and various numbers // of watches and listeners int[] watchCounts = new int[] {1, 5, 10}; int[] listenerCounts = new int[] {1, 5, 10}; for (int ordering = 0; ordering < 3; ordering++) { for (int watchCount : watchCounts) { for (int listenerCount : listenerCounts) { Map<Watch, WatchEntry> map = new HashMap<Watch, WatchEntry>(); check(map, ordering); setup(map, watchCount, listenerCount); populate(registry, map, ordering); check(map, ordering); clear(registry, map); check(new HashMap<Watch, WatchEntry>(), ordering); // Check what happens if watches are re-registered // and listeners are re-added populate(registry, map, ordering); check(map, ordering); clear(registry, map); check(new HashMap<Watch, WatchEntry>(), ordering); } } } try { registry.addThresholdListener(null, new LoggingThresholdListener()); Assert.fail("IllegalArgumentException expected but not thrown"); } catch (IllegalArgumentException e) { } try { registry.addThresholdListener("aaa", null); Assert.fail("IllegalArgumentException expected but not thrown"); } catch (IllegalArgumentException e) { } } private void setup(Map<Watch, WatchEntry> map, int watchCount, int listenerCount) { for (int i = 0; i < watchCount; i++) { String id = "watch" + i; // Try empty id for one of the watches if (i == watchCount - 3 && i != 0) { id = ""; } // We are specifying the data source explicitly because // otherwise every watch would use an exported proxy wich // gets unexported when the watch is deregistered, // therefore emitting annoying exceptions and slowing // the test badly WatchDataSource ds = new WatchDataSourceImpl( id, EmptyConfiguration.INSTANCE); ThresholdWatch watch = new GaugeWatch(ds, id); watch.setThresholdValues(new ThresholdValues(0, 1)); WatchEntry entry = new WatchEntry(); for (int j = 0; j < listenerCount; j++) { entry.listeners.add(new LoggingThresholdListener()); } // No listeners for one of the watches if (i == watchCount - 2 && i != 0) { entry.listeners = new ArrayList<ThresholdListener>(); } // Not registering the last watch. This tests adding // listeners for id that never appears in the registry if (i == watchCount - 1 && i != 0) { entry.doNotRegister = true; } map.put(watch, entry); } } private void populate(WatchDataSourceRegistry registry, Map<Watch, WatchEntry> map, int ordering) { if (ordering == 0) { // All listeners first for (Map.Entry<Watch, WatchEntry> entry : map.entrySet()) { Watch watch = entry.getKey(); WatchEntry wEntry = entry.getValue(); for (ThresholdListener listener : wEntry.listeners) { registry.addThresholdListener(watch.getId(), listener); } } for (Map.Entry<Watch, WatchEntry> entry : map.entrySet()) { Watch watch = entry.getKey(); WatchEntry wEntry = entry.getValue(); if (!wEntry.doNotRegister) { registry.register(watch); } } } else if(ordering == 1) { // All watches first for (Map.Entry<Watch, WatchEntry> entry : map.entrySet()) { Watch watch = entry.getKey(); WatchEntry wEntry = entry.getValue(); if (!wEntry.doNotRegister) { registry.register(watch); } } for (Map.Entry<Watch, WatchEntry> e : map.entrySet()) { Watch watch = e.getKey(); WatchEntry entry = e.getValue(); for (ThresholdListener listener : entry.listeners) { registry.addThresholdListener(watch.getId(), listener); } } } else { // Interleaving boolean watchFirst = true; for (Map.Entry<Watch, WatchEntry> e : map.entrySet()) { Watch watch = e.getKey(); WatchEntry entry = e.getValue(); if (watchFirst) { if (!entry.doNotRegister) { registry.register(watch); } } for (ThresholdListener listener : entry.listeners) { registry.addThresholdListener(watch.getId(), listener); } if (!watchFirst) { if (!entry.doNotRegister) { registry.register(watch); } } watchFirst = !watchFirst; } } } private void clear(WatchDataSourceRegistry registry, Map<Watch, WatchEntry> map) { for (Watch w : map.keySet()) { registry.deregister(w); } } private void check(Map<Watch, WatchEntry> map, int ordering) { // This method needs to know the population index because // the effect of using the watch registry changes from // population to population. This is mainly because the // watch registry does not cleanup the id <-> listener // mapping when watches are deregistered). boolean watchFirst = true; for (Map.Entry<Watch, WatchEntry> e : map.entrySet()) { Watch watch = e.getKey(); String id = watch.getId(); watch.addWatchRecord(new Calculable(id, 1.1)); watch.addWatchRecord(new Calculable(id, 0.5)); WatchEntry entry = e.getValue(); List listeners = entry.listeners; for (int j = 0; j < listeners.size(); j++) { LoggingThresholdListener lnr = (LoggingThresholdListener) listeners.get(j); String stm = "setThresholdManager()"; String prefix; if (ordering == 0) { prefix = replicate(stm, 1); } else if (ordering == 1) { int base = listeners.size(); prefix = replicate(stm, base - j); } else { if (watchFirst) { int base = listeners.size(); prefix = replicate(stm, base - j); } else { prefix = replicate(stm, 1); } } if (!entry.doNotRegister) { checkLog(lnr.log(), prefix + "breach(" + id + ":1.1)" + "clear(" + id + ":0.5)"); } else { checkLog(lnr.log(), ""); } } watchFirst = !watchFirst; } } /* * Replicates a given string a given number of times. */ private String replicate(String s, int count) { String res = ""; for (int i = 0; i < count; i++) { res += s; } return res; } /** * The class holds data that describes one watch used in the test. */ private class WatchEntry { public List<ThresholdListener> listeners = new ArrayList<ThresholdListener>(); public boolean doNotRegister = false; } /** * The class extends <code>WatchDataSourceImpl</code> and logs * all calls to the <code>close()</code> method into a string buffer. */ private class LoggingWatchDataSource extends WatchDataSourceImpl { private StringBuffer log = new StringBuffer(); public LoggingWatchDataSource(String id, Configuration config) { super(id, config); } public StringBuffer log() { return log; } public void close() { log.append("close()"); super.close(); } } /** * The class provides a mock implementation of the * <code>ThresholdListener</code> interface. The main function * of the class is to receive threshold events and log them * into a string buffer. */ private class LoggingThresholdListener implements SettableThresholdListener { StringBuffer log; public LoggingThresholdListener() { this.log = new StringBuffer(); } public void notify(Calculable calculable, ThresholdValues thresholdValues, ThresholdType type) { log.append(type == ThresholdType.BREACHED ? "breach(" : "clear("); log.append(calculable.getId()); log.append(":"); log.append(calculable.getValue()); log.append(")"); } public void setThresholdManager(ThresholdManager thresholdManager) { log.append("setThresholdManager()"); thresholdManager.addThresholdListener(this); } public StringBuffer log() { return log; } public String getID() { return null; } } /* * Checks that a given log holds a given string, * and clears the log. */ private void checkLog(StringBuffer log, String s) { Assert.assertEquals(s, log.toString()); log.delete(0, log.length()); } }