Clover coverage report - Code Coverage for hivemind release 1.1.1
Coverage timestamp: Sat Jan 28 2006 10:19:31 PST
file stats: LOC: 718   Methods: 42
NCLOC: 360   Classes: 5
 
 Source file Conditionals Statements Methods TOTAL
HiveMindTestCase.java 85.4% 90.2% 88.1% 88.8%
coverage coverage
 1    // Copyright 2004, 2005 The Apache Software Foundation
 2    //
 3    // Licensed under the Apache License, Version 2.0 (the "License");
 4    // you may not use this file except in compliance with the License.
 5    // You may obtain a copy of the License at
 6    //
 7    // http://www.apache.org/licenses/LICENSE-2.0
 8    //
 9    // Unless required by applicable law or agreed to in writing, software
 10    // distributed under the License is distributed on an "AS IS" BASIS,
 11    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    // See the License for the specific language governing permissions and
 13    // limitations under the License.
 14   
 15    package org.apache.hivemind.test;
 16   
 17    import java.net.URL;
 18    import java.util.ArrayList;
 19    import java.util.Iterator;
 20    import java.util.List;
 21    import java.util.Locale;
 22   
 23    import junit.framework.AssertionFailedError;
 24    import junit.framework.TestCase;
 25   
 26    import org.apache.hivemind.ApplicationRuntimeException;
 27    import org.apache.hivemind.ClassResolver;
 28    import org.apache.hivemind.Location;
 29    import org.apache.hivemind.ModuleDescriptorProvider;
 30    import org.apache.hivemind.Registry;
 31    import org.apache.hivemind.Resource;
 32    import org.apache.hivemind.impl.DefaultClassResolver;
 33    import org.apache.hivemind.impl.LocationImpl;
 34    import org.apache.hivemind.impl.RegistryBuilder;
 35    import org.apache.hivemind.impl.XmlModuleDescriptorProvider;
 36    import org.apache.hivemind.internal.ser.ServiceSerializationHelper;
 37    import org.apache.hivemind.util.ClasspathResource;
 38    import org.apache.hivemind.util.PropertyUtils;
 39    import org.apache.hivemind.util.URLResource;
 40    import org.apache.log4j.Level;
 41    import org.apache.log4j.LogManager;
 42    import org.apache.log4j.Logger;
 43    import org.apache.log4j.spi.LoggingEvent;
 44    import org.apache.oro.text.regex.Pattern;
 45    import org.apache.oro.text.regex.Perl5Compiler;
 46    import org.apache.oro.text.regex.Perl5Matcher;
 47    import org.easymock.MockControl;
 48    import org.easymock.classextension.MockClassControl;
 49   
 50    /**
 51    * Contains some support for creating HiveMind tests; this is useful enough that has been moved into
 52    * the main framework, to simplify creation of tests in the dependent libraries.
 53    *
 54    * @author Howard Lewis Ship
 55    */
 56    public abstract class HiveMindTestCase extends TestCase
 57    {
 58    // /CLOVER:OFF
 59   
 60    /**
 61    * An instance of {@link DefaultClassResolver} that can be used by tests.
 62    */
 63   
 64    private ClassResolver _classResolver;
 65   
 66    protected String _interceptedLoggerName;
 67   
 68    protected StoreAppender _appender;
 69   
 70    private static Perl5Compiler _compiler;
 71   
 72    private static Perl5Matcher _matcher;
 73   
 74    /** List of {@link org.easymock.MockControl}. */
 75   
 76    private List _controls = new ArrayList();
 77   
 78    /** @since 1.1 */
 79    interface MockControlFactory
 80    {
 81    public MockControl newControl(Class mockClass);
 82    }
 83   
 84    /** @since 1.1 */
 85    private static class InterfaceMockControlFactory implements MockControlFactory
 86    {
 87  219 public MockControl newControl(Class mockClass)
 88    {
 89  219 return MockControl.createStrictControl(mockClass);
 90    }
 91    }
 92   
 93    /** @since 1.1 */
 94    private static class ClassMockControlFactory implements MockControlFactory
 95    {
 96  1 public MockControl newControl(Class mockClass)
 97    {
 98  1 return MockClassControl.createStrictControl(mockClass);
 99    }
 100    }
 101   
 102    /** @since 1.1 */
 103    static class PlaceholderClassMockControlFactory implements MockControlFactory
 104    {
 105  1 public MockControl newControl(Class mockClass)
 106    {
 107  1 throw new RuntimeException(
 108    "Unable to instantiate EasyMock control for "
 109    + mockClass
 110    + "; ensure that easymockclassextension-1.1.jar and cglib-full-2.0.1.jar are on the classpath.");
 111    }
 112    }
 113   
 114    /** @since 1.1 */
 115    private static final MockControlFactory _interfaceMockControlFactory = new InterfaceMockControlFactory();
 116   
 117    /** @since 1.1 */
 118    private static MockControlFactory _classMockControlFactory;
 119   
 120    static
 121    {
 122  1 try
 123    {
 124  1 _classMockControlFactory = new ClassMockControlFactory();
 125    }
 126    catch (NoClassDefFoundError ex)
 127    {
 128  0 _classMockControlFactory = new PlaceholderClassMockControlFactory();
 129    }
 130    }
 131   
 132    /**
 133    * Returns the given file as a {@link Resource} from the classpath. Typically, this is to find
 134    * files in the same folder as the invoking class.
 135    */
 136  152 protected Resource getResource(String file)
 137    {
 138  152 URL url = getClass().getResource(file);
 139   
 140  152 if (url == null)
 141  0 throw new NullPointerException("No resource named '" + file + "'.");
 142   
 143  152 return new URLResource(url);
 144    }
 145   
 146    /**
 147    * Converts the actual list to an array and invokes
 148    * {@link #assertListsEqual(Object[], Object[])}.
 149    */
 150  16 protected static void assertListsEqual(Object[] expected, List actual)
 151    {
 152  16 assertListsEqual(expected, actual.toArray());
 153    }
 154   
 155    /**
 156    * Asserts that the two arrays are equal; same length and all elements equal. Checks the
 157    * elements first, then the length.
 158    */
 159  23 protected static void assertListsEqual(Object[] expected, Object[] actual)
 160    {
 161  23 assertNotNull(actual);
 162   
 163  23 int min = Math.min(expected.length, actual.length);
 164   
 165  23 for (int i = 0; i < min; i++)
 166  67 assertEquals("list[" + i + "]", expected[i], actual[i]);
 167   
 168  23 assertEquals("list length", expected.length, actual.length);
 169    }
 170   
 171    /**
 172    * Called when code should not be reachable (because a test is expected to throw an exception);
 173    * throws AssertionFailedError always.
 174    */
 175  0 protected static void unreachable()
 176    {
 177  0 throw new AssertionFailedError("This code should be unreachable.");
 178    }
 179   
 180    /**
 181    * Sets up an appender to intercept logging for the specified logger. Captured log events can be
 182    * recovered via {@link #getInterceptedLogEvents()}.
 183    */
 184  49 protected void interceptLogging(String loggerName)
 185    {
 186  49 Logger logger = LogManager.getLogger(loggerName);
 187   
 188  49 logger.removeAllAppenders();
 189   
 190  49 _interceptedLoggerName = loggerName;
 191  49 _appender = new StoreAppender();
 192  49 _appender.activateOptions();
 193   
 194  49 logger.setLevel(Level.DEBUG);
 195  49 logger.setAdditivity(false);
 196  49 logger.addAppender(_appender);
 197    }
 198   
 199    /**
 200    * Gets the list of events most recently intercepted. This resets the appender, clearing the
 201    * list of stored events.
 202    *
 203    * @see #interceptLogging(String)
 204    */
 205   
 206  56 protected List getInterceptedLogEvents()
 207    {
 208  56 return _appender.getEvents();
 209    }
 210   
 211    /**
 212    * Removes the appender that may have been setup by {@link #interceptLogging(String)}. Also,
 213    * invokes {@link org.apache.hivemind.util.PropertyUtils#clearCache()}.
 214    */
 215  616 protected void tearDown() throws Exception
 216    {
 217  616 super.tearDown();
 218   
 219  616 if (_appender != null)
 220    {
 221  49 _appender = null;
 222   
 223  49 Logger logger = LogManager.getLogger(_interceptedLoggerName);
 224  49 logger.setLevel(null);
 225  49 logger.setAdditivity(true);
 226  49 logger.removeAllAppenders();
 227    }
 228   
 229  616 PropertyUtils.clearCache();
 230   
 231  616 ServiceSerializationHelper.setServiceSerializationSupport(null);
 232    }
 233   
 234    /**
 235    * Checks that the provided substring exists in the exceptions message.
 236    */
 237  51 protected void assertExceptionSubstring(Throwable ex, String substring)
 238    {
 239  51 String message = ex.getMessage();
 240  51 assertNotNull(message);
 241   
 242  51 int pos = message.indexOf(substring);
 243   
 244  51 if (pos < 0)
 245  0 throw new AssertionFailedError("Exception message (" + message + ") does not contain ["
 246    + substring + "]");
 247    }
 248   
 249    /**
 250    * Checks that the message for an exception matches a regular expression.
 251    */
 252   
 253  1 protected void assertExceptionRegexp(Throwable ex, String pattern) throws Exception
 254    {
 255  1 String message = ex.getMessage();
 256  1 assertNotNull(message);
 257   
 258  1 setupMatcher();
 259   
 260  1 Pattern compiled = _compiler.compile(pattern);
 261   
 262  1 if (_matcher.contains(message, compiled))
 263  1 return;
 264   
 265  0 throw new AssertionFailedError("Exception message (" + message
 266    + ") does not contain regular expression [" + pattern + "].");
 267    }
 268   
 269  1 protected void assertRegexp(String pattern, String actual) throws Exception
 270    {
 271  1 setupMatcher();
 272   
 273  1 Pattern compiled = _compiler.compile(pattern);
 274   
 275  1 if (_matcher.contains(actual, compiled))
 276  1 return;
 277   
 278  0 throw new AssertionFailedError("\"" + actual + "\" does not contain regular expression["
 279    + pattern + "].");
 280    }
 281   
 282    /**
 283    * Digs down through (potentially) a stack of ApplicationRuntimeExceptions until it reaches the
 284    * originating exception, which is returned.
 285    */
 286  4 protected Throwable findNestedException(ApplicationRuntimeException ex)
 287    {
 288  4 Throwable cause = ex.getRootCause();
 289   
 290  4 if (cause == null || cause == ex)
 291  1 return ex;
 292   
 293  3 if (cause instanceof ApplicationRuntimeException)
 294  2 return findNestedException((ApplicationRuntimeException) cause);
 295   
 296  1 return cause;
 297    }
 298   
 299    /**
 300    * Checks to see if a specific event matches the name and message.
 301    *
 302    * @param message
 303    * exact message to search for
 304    * @param events
 305    * the list of events {@link #getInterceptedLogEvents()}
 306    * @param index
 307    * the index to check at
 308    */
 309  42 private void assertLoggedMessage(String message, List events, int index)
 310    {
 311  42 LoggingEvent e = (LoggingEvent) events.get(index);
 312   
 313  42 assertEquals("Message", message, e.getMessage());
 314    }
 315   
 316    /**
 317    * Checks the messages for all logged events for exact match against the supplied list.
 318    */
 319  8 protected void assertLoggedMessages(String[] messages)
 320    {
 321  8 List events = getInterceptedLogEvents();
 322   
 323  8 for (int i = 0; i < messages.length; i++)
 324    {
 325  42 assertLoggedMessage(messages[i], events, i);
 326    }
 327    }
 328   
 329    /**
 330    * Asserts that some capture log event matches the given message exactly.
 331    */
 332  24 protected void assertLoggedMessage(String message)
 333    {
 334  24 assertLoggedMessage(message, getInterceptedLogEvents());
 335    }
 336   
 337    /**
 338    * Asserts that some capture log event matches the given message exactly.
 339    *
 340    * @param message
 341    * to search for; success is finding a logged message contain the parameter as a
 342    * substring
 343    * @param events
 344    * from {@link #getInterceptedLogEvents()}
 345    */
 346  26 protected void assertLoggedMessage(String message, List events)
 347    {
 348  26 int count = events.size();
 349   
 350  182 for (int i = 0; i < count; i++)
 351    {
 352  182 LoggingEvent e = (LoggingEvent) events.get(i);
 353   
 354  182 String eventMessage = String.valueOf(e.getMessage());
 355   
 356  182 if (eventMessage.indexOf(message) >= 0)
 357  26 return;
 358    }
 359   
 360  0 throw new AssertionFailedError("Could not find logged message: " + message);
 361    }
 362   
 363  21 protected void assertLoggedMessagePattern(String pattern) throws Exception
 364    {
 365  21 assertLoggedMessagePattern(pattern, getInterceptedLogEvents());
 366    }
 367   
 368  26 protected void assertLoggedMessagePattern(String pattern, List events) throws Exception
 369    {
 370  26 setupMatcher();
 371   
 372  26 Pattern compiled = null;
 373   
 374  26 int count = events.size();
 375   
 376  492 for (int i = 0; i < count; i++)
 377    {
 378  492 LoggingEvent e = (LoggingEvent) events.get(i);
 379   
 380  492 String eventMessage = e.getMessage().toString();
 381   
 382  492 if (compiled == null)
 383  26 compiled = _compiler.compile(pattern);
 384   
 385  492 if (_matcher.contains(eventMessage, compiled))
 386  26 return;
 387   
 388    }
 389   
 390  0 throw new AssertionFailedError("Could not find logged message with pattern: " + pattern);
 391    }
 392   
 393  29 private void setupMatcher()
 394    {
 395  29 if (_compiler == null)
 396  1 _compiler = new Perl5Compiler();
 397   
 398  29 if (_matcher == null)
 399  1 _matcher = new Perl5Matcher();
 400    }
 401   
 402    /**
 403    * Convienience method for invoking {@link #buildFrameworkRegistry(String[])} with only a single
 404    * file.
 405    */
 406  103 protected Registry buildFrameworkRegistry(String file) throws Exception
 407    {
 408  103 return buildFrameworkRegistry(new String[]
 409    { file });
 410    }
 411   
 412    /**
 413    * Builds a minimal registry, containing only the specified files, plus the master module
 414    * descriptor (i.e., those visible on the classpath). Files are resolved using
 415    * {@link HiveMindTestCase#getResource(String)}.
 416    */
 417  107 protected Registry buildFrameworkRegistry(String[] files) throws Exception
 418    {
 419  107 ClassResolver resolver = getClassResolver();
 420   
 421  107 List descriptorResources = new ArrayList();
 422  107 for (int i = 0; i < files.length; i++)
 423    {
 424  113 Resource resource = getResource(files[i]);
 425   
 426  113 descriptorResources.add(resource);
 427    }
 428   
 429  107 ModuleDescriptorProvider provider = new XmlModuleDescriptorProvider(resolver,
 430    descriptorResources);
 431   
 432  107 return buildFrameworkRegistry(provider);
 433    }
 434   
 435    /**
 436    * Builds a registry, containing only the modules delivered by the specified
 437    * {@link org.apache.hivemind.ModuleDescriptorProvider}, plus the master module descriptor
 438    * (i.e., those visible on the classpath).
 439    */
 440  112 protected Registry buildFrameworkRegistry(ModuleDescriptorProvider customProvider)
 441    {
 442  112 ClassResolver resolver = getClassResolver();
 443   
 444  112 RegistryBuilder builder = new RegistryBuilder();
 445   
 446  112 builder.addModuleDescriptorProvider(new XmlModuleDescriptorProvider(resolver));
 447  112 builder.addModuleDescriptorProvider(customProvider);
 448   
 449  112 return builder.constructRegistry(Locale.getDefault());
 450    }
 451   
 452    /**
 453    * Builds a registry from exactly the provided resource; this registry will not include the
 454    * <code>hivemind</code> module.
 455    */
 456  0 protected Registry buildMinimalRegistry(Resource l) throws Exception
 457    {
 458  0 RegistryBuilder builder = new RegistryBuilder();
 459   
 460  0 return builder.constructRegistry(Locale.getDefault());
 461    }
 462   
 463    /**
 464    * Creates a <em>managed</em> control via
 465    * {@link MockControl#createStrictControl(java.lang.Class)}. The created control is remembered,
 466    * and will be invoked by {@link #replayControls()}, {@link #verifyControls()}, etc.
 467    * <p>
 468    * The class to mock may be either an interface or a class. The EasyMock class extension
 469    * (easymockclassextension-1.1.jar) and CGLIB (cglib-full-2.01.jar) must be present in the
 470    * latter case (new since 1.1).
 471    * <p>
 472    * This method is not deprecated, but is rarely used; typically {@link #newMock(Class)} is used
 473    * to create the control and the mock, and {@link #setReturnValue(Object, Object)} and
 474    * {@link #setThrowable(Object, Throwable)} are used to while training it.
 475    * {@link #getControl(Object)} is used for the rare cases where the MockControl itself is
 476    * needed.
 477    */
 478  220 protected MockControl newControl(Class mockClass)
 479    {
 480  220 MockControlFactory factory = mockClass.isInterface() ? _interfaceMockControlFactory
 481    : _classMockControlFactory;
 482   
 483  220 MockControl result = factory.newControl(mockClass);
 484   
 485  220 addControl(result);
 486   
 487  220 return result;
 488    }
 489   
 490    /**
 491    * Accesses the control for a previously created mock object. Iterates over the list of managed
 492    * controls until one is found whose mock object identity equals the mock object provided.
 493    *
 494    * @param Mock
 495    * object whose control is needed
 496    * @return the corresponding MockControl if found
 497    * @throws IllegalArgumentException
 498    * if not found
 499    * @since 1.1
 500    */
 501   
 502  70 protected MockControl getControl(Object mock)
 503    {
 504  70 Iterator i = _controls.iterator();
 505  114 while (i.hasNext())
 506    {
 507  114 MockControl control = (MockControl) i.next();
 508   
 509  114 if (control.getMock() == mock)
 510  70 return control;
 511    }
 512   
 513  0 throw new IllegalArgumentException(mock
 514    + " is not a mock object controlled by any registered MockControl instance.");
 515    }
 516   
 517    /**
 518    * Invoked when training a mock object to set the Throwable for the most recently invoked
 519    * method.
 520    *
 521    * @param mock
 522    * the mock object being trained
 523    * @param t
 524    * the exception the object should throw when it replays
 525    * @since 1.1
 526    */
 527  1 protected void setThrowable(Object mock, Throwable t)
 528    {
 529  1 getControl(mock).setThrowable(t);
 530    }
 531   
 532    /**
 533    * Invoked when training a mock object to set the return value for the most recently invoked
 534    * method. Overrides of this method exist to support a number of primitive types.
 535    *
 536    * @param mock
 537    * the mock object being trained
 538    * @param returnValue
 539    * the value to return from the most recently invoked methods
 540    * @since 1.1
 541    */
 542  51 protected void setReturnValue(Object mock, Object returnValue)
 543    {
 544  51 getControl(mock).setReturnValue(returnValue);
 545    }
 546   
 547    /**
 548    * Invoked when training a mock object to set the return value for the most recently invoked
 549    * method. Overrides of this method exist to support a number of primitive types.
 550    *
 551    * @param mock
 552    * the mock object being trained
 553    * @param returnValue
 554    * the value to return from the most recently invoked methods
 555    * @since 1.1
 556    */
 557  0 protected void setReturnValue(Object mock, long returnValue)
 558    {
 559  0 getControl(mock).setReturnValue(returnValue);
 560    }
 561   
 562    /**
 563    * Invoked when training a mock object to set the return value for the most recently invoked
 564    * method. Overrides of this method exist to support a number of primitive types.
 565    *
 566    * @param mock
 567    * the mock object being trained
 568    * @param returnValue
 569    * the value to return from the most recently invoked methods
 570    * @since 1.1
 571    */
 572  0 protected void setReturnValue(Object mock, float returnValue)
 573    {
 574  0 getControl(mock).setReturnValue(returnValue);
 575    }
 576   
 577    /**
 578    * Invoked when training a mock object to set the return value for the most recently invoked
 579    * method. Overrides of this method exist to support a number of primitive types.
 580    *
 581    * @param mock
 582    * the mock object being trained
 583    * @param returnValue
 584    * the value to return from the most recently invoked methods
 585    * @since 1.1
 586    */
 587  0 protected void setReturnValue(Object mock, double returnValue)
 588    {
 589  0 getControl(mock).setReturnValue(returnValue);
 590    }
 591   
 592    /**
 593    * Invoked when training a mock object to set the return value for the most recently invoked
 594    * method. Overrides of this method exist to support a number of primitive types.
 595    *
 596    * @param mock
 597    * the mock object being trained
 598    * @param returnValue
 599    * the value to return from the most recently invoked methods
 600    * @since 1.1
 601    */
 602  16 protected void setReturnValue(Object mock, boolean returnValue)
 603    {
 604  16 getControl(mock).setReturnValue(returnValue);
 605    }
 606   
 607    /**
 608    * Adds the control to the list of managed controls used by {@link #replayControls()} and
 609    * {@link #verifyControls()}.
 610    */
 611  221 protected void addControl(MockControl control)
 612    {
 613  221 _controls.add(control);
 614    }
 615   
 616    /**
 617    * Convienience for invoking {@link #newControl(Class)} and then invoking
 618    * {@link MockControl#getMock()} on the result.
 619    */
 620  104 protected Object newMock(Class mockClass)
 621    {
 622  104 return newControl(mockClass).getMock();
 623    }
 624   
 625    /**
 626    * Invokes {@link MockControl#replay()} on all controls created by {@link #newControl(Class)}.
 627    */
 628  134 protected void replayControls()
 629    {
 630  134 Iterator i = _controls.iterator();
 631  134 while (i.hasNext())
 632    {
 633  304 MockControl c = (MockControl) i.next();
 634  304 c.replay();
 635    }
 636    }
 637   
 638    /**
 639    * Invokes {@link org.easymock.MockControl#verify()} and {@link MockControl#reset()} on all
 640    * controls created by {@link #newControl(Class)}.
 641    */
 642   
 643  132 protected void verifyControls()
 644    {
 645  132 Iterator i = _controls.iterator();
 646  132 while (i.hasNext())
 647    {
 648  301 MockControl c = (MockControl) i.next();
 649  301 c.verify();
 650  301 c.reset();
 651    }
 652    }
 653   
 654    /**
 655    * Invokes {@link org.easymock.MockControl#reset()} on all controls.
 656    */
 657   
 658  1 protected void resetControls()
 659    {
 660  1 Iterator i = _controls.iterator();
 661  1 while (i.hasNext())
 662    {
 663  2 MockControl c = (MockControl) i.next();
 664  2 c.reset();
 665    }
 666    }
 667   
 668    /**
 669    * @deprecated To be removed in 1.2. Use {@link #newLocation()} instead.
 670    */
 671  38 protected Location fabricateLocation(int line)
 672    {
 673  38 String path = "/" + getClass().getName().replace('.', '/');
 674   
 675  38 Resource r = new ClasspathResource(getClassResolver(), path);
 676   
 677  38 return new LocationImpl(r, line);
 678    }
 679   
 680    private int _line = 1;
 681   
 682    /**
 683    * Returns a new {@link Location} instance. The resource is the test class, and the line number
 684    * increments by one from one for each invocation (thus each call will get a unique instance not
 685    * equal to any previously obtained instance).
 686    *
 687    * @since 1.1
 688    */
 689  38 protected Location newLocation()
 690    {
 691  38 return fabricateLocation(_line++);
 692    }
 693   
 694    /**
 695    * Returns a {@link DefaultClassResolver}. Repeated calls in the same test return the same
 696    * value.
 697    *
 698    * @since 1.1
 699    */
 700   
 701  291 protected ClassResolver getClassResolver()
 702    {
 703  291 if (_classResolver == null)
 704  156 _classResolver = new DefaultClassResolver();
 705   
 706  291 return _classResolver;
 707    }
 708   
 709  1 protected boolean matches(String input, String pattern) throws Exception
 710    {
 711  1 setupMatcher();
 712   
 713  1 Pattern compiled = _compiler.compile(pattern);
 714   
 715  1 return _matcher.matches(input, compiled);
 716    }
 717   
 718    }